使用烧瓶中的 send_file() 时文件损坏,pymongo gridfs 中的数据

Posted

技术标签:

【中文标题】使用烧瓶中的 send_file() 时文件损坏,pymongo gridfs 中的数据【英文标题】:File corrupted when using send_file() from flask, data from pymongo gridfs 【发布时间】:2021-11-07 00:56:03 【问题描述】:

好吧,我的英文不好,而且标题可能看起来很奇怪。

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

文件上传、文档插入功能都没有问题,奇怪的是flasksend_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。但是响应正文返回 549.61KB,并且图像已损坏。但是如果我只是直接将文件写入我的磁盘

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

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

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

search the corrupted file head in the correct file

我尝试使用Blob 对象并从浏览器下载它。没有区别。

我的代码有什么问题还是我误用了send_file()?还是应该使用flask_pymongo

【问题讨论】:

【参考方案1】:

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

我就是这样解决的

...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() 查找光标的位置时,它返回4096。所以当我在send_file() 中再次调用file.read() 时,光标从4096B 读取到文件头。 549+4=553,这就是问题所在。

最后我在每次 read() 操作后将光标设置为位置 0,它会返回正确的文件。

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

【讨论】:

以上是关于使用烧瓶中的 send_file() 时文件损坏,pymongo gridfs 中的数据的主要内容,如果未能解决你的问题,请参考以下文章

send_file()后烧瓶无法删除文件[重复]

烧瓶 send_file 不适用于 tar.gz 文件

在 Pythonanywhere 上的烧瓶中替代 send_file()?

尽管有 200 条消息,flask send_file 仍然失败 [重复]

使用 mysql 和烧瓶登录的 Flask-sqlalchemy 损坏管道错误 32

使用Python Flask中的send_file发送视频时移动设备发生错误http://mattspitz.me/2013/11/20/serving-authenticated-media-wit