使用来自 Flask 的 send_file() 时文件损坏,来自 pymongo gridfs 的数据

File corrupted when using send_file() from flask, data from pymongo gridfs

我的英语不好,标题可能看起来很奇怪。

反正我现在正在用flask搭建一个可以存储文件的网站,mongodb就是数据库

文件上传,文档插入功能没有问题,奇怪的是从flask send_file()发送的文件被无故截断了。这是我的代码

from flask import ..., send_file, ...
import pymongo
import gridfs

#...

@app.route("/record/download/<record_id>")
def api_softwares_record_download(record_id):
    try:
        #...
        file = files_gridfs.find_one({"_id": record_id})
        file_ext = filetype.guess_extension(file.read(2048))
        filename = "{}-{}{}".format(
            app["name"],
            record["version"],
            ".{}".format(file_ext) if file_ext else "",
        )
        response = send_file(file, as_attachment=True, attachment_filename=filename)
        return response
    except ...

例如,原始图像文件为553KB。但是响应 body returns 549.61KB,并且图像被破坏了。但是如果我直接把文件写到我的磁盘

#...
with open('test.png', 'wb+') as file:
    file.write(files_gridfs.find_one({"_id": record_id}).read())

图片文件大小为553KB,图片可读。

当我用 VS Code 的文本编辑器比较这两个文件时,我发现正确的文件以 �PNG 开头,但损坏的文件以 �ϟ8���>�L�y

开头

search the corrupted file head in the correct file

我尝试使用 Blob object 从浏览器下载。没有区别。

是我的代码有问题还是我用错了send_file()?或者我应该使用 flask_pymongo?

有趣的是,我发现我的代码有什么问题。

我就是这样解决的

...file.read(2048)
file.seek(0)
...
file.read(2048)
file.seek(0)
...
response = send_file(file, ...)
return response

原因如下:

由于某些原因,我使用filetype来检测文件的扩展名和mime类型,所以我将2048B发送给filetype进行检测。

file_ext = filetype.guess_extension(file.read(2048))
file_mime = filetype.guess_mime(file.read(2048)) #this line wasn't copied in my question. My fault.

而且我刚刚从pymongo API了解到python(或pymongogridfs,之前完全不知道)使用游标读取文件。当我尝试使用 file.seek() 查找光标的位置时,它 returns 4096。所以当我在send_file()中再次调用file.read()时,光标从4096B读取到文件头。 549+4=553,问题来了

最后,我在每次 read() 操作后将光标设置到位置 0,它 returns 是正确的文件。

如果你和我一样犯了同样的错误,希望这对你有所帮助。