Rails:上传文件的校验和

6

我正在使用Ruby on Rails生成上传图片的校验和(sha256)。

upload = params[:file]
data1 = upload.read
data2 = File.read(upload.tempfile)
checksum1 = Digest::SHA256.hexdigest(data1)
checksum2 = Digest::SHA256.hexdigest(data2)
puts checksum1
puts checksum2

最后两个语句返回了不同的值。 checksum1是通过使用UploadedFile对象读取数据生成的。 checksum2是通过读取文件系统中的临时文件生成的。
ActionDispatch :: Http :: UploadedFile对象是否返回除上传文件内容之外的其他内容?当我生成已写入文件系统的上传文件的校验和时,它与checksum2(临时文件校验和)匹配,而不是与checksum1(UploadedFile.read)匹配。
我假设通过从文件系统中读取临时文件生成的校验和更可靠,因为对象(UploadedFile)实现可能会更改。如果需要,可以更容易地生成现有文件在文件系统上的校验和。
那么,checksums的差异原因是什么,哪一个更可靠?
谢谢。
更新1: 根据@pablo-castellazzi的建议,我使用Digest :: SHA256.file(upload.path)。hexdigest生成了哈希值。让我们称此为checksum3
这个checksum3等于checksum1但与checksum2不同。
更新2:如果我使用二进制模式读取文件,就像@Arsen7所提到的那样,那么所有的checksums都相等。

4
为了以后参考,获取文件校验和的更好方法是 Digest::SHA256.file(path_to_file).hexdigest。使用 File.read 可能会导致内存不足错误。 - Pablo Castellazzi
2个回答

2
你是否比较了"data1"和"data2"的内容?试着将它们保存到文件中并查看。
我想你可能需要在第一次读取之前调用 "upload.rewind",但首要任务是查看从文件中读取的原始数据。
更新:
你没有说你在Windows上。在这种情况下,你应该小心并以所谓的“二进制”模式读取文件。
将"File.read"方法更改为类似以下内容的方法:
data2 = nil
File.open(upload.path, "rb") {|f| data2 = f.read }

(实现Pablo Castellazzi的建议,使用.path方法)

我建议您在某些二进制安全编辑器中打开文件(例如vim),并比较不同之处。您会注意到,也许大部分数据相同,但在其中一个文件中,行结束符不同,或者您可能会发现其他一些差异。

在Windows的情况下,最常见的问题是二进制模式。


当我将data1和data2写入磁盘时,data1显示为正确的图像,而data2则变形。 - Srisa
谢谢您,二进制模式没错。 :) 在非Windows机器上我可以使用'rb'模式吗? - Srisa
可以的。在其他系统上,它会被静默忽略。请不要称呼我“先生”。我太谦虚了,不配这个头衔 ;-)) - Arsen7
这对我也起作用了 - 一个跨平台的 Ruby 应用程序 - Linux 和 Windows。在 Windows 上有所不同,直到我使用了这个。 - Ben

1

假设您正在使用Rails 3.x,data1校验和是正确的。 data2内容应该使用以下方式读取:

data2 = File.read(upload.path)

upload.tempfile是保存文件对象实例的变量,而不是临时文件的路径。

这里是相关的实现细节。

此外,这很奇怪,因为File.read(File.read)应该会抛出有关文件未找到或无效文件名的异常。


tempfile 包含临时文件的路径。以下是日志中的数据:[对于不存在的格式化表示歉意] 参数:“file”=>#<ActionDispatch::Http::UploadedFile:0x1c0c638 @original_filename="lorry.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name="file";filename="lorry.jpg"\r\nContent-Type: image/jpeg\r\n", @tempfile=#File:C:/DOCUME~1/User1/LOCALS~1/Temp/RackMultipart20110621-2496-1lnrcpi>} - Srisa
并不完全是这样。正如您从表示中看到的那样,@tempfile是一个File实例,指向“C:/ DOCUME〜1 / User1 / LOCALS〜1 / Temp / RackMultipart20110621-2496-1lnrcpi”。路径只是一个字符串,它的表示应该是类似于@tempfile =“C:/ DOCUME〜1 / User1 / LOCALS〜1 / Temp / RackMultipart20110621-2496-1lnrcpi”的东西。 - Pablo Castellazzi
你是对的。我已经使用了“path”,它按预期工作。也许 File 对象上的 to_s 方法返回路径... - Srisa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接