【问题标题】:Unable to get the right file name using multipart headers无法使用多部分标题获取正确的文件名
【发布时间】:2020-04-24 15:39:09
【问题描述】:

我正在开发一种允许上传和下载文件的个人云项目(具有类似谷歌的搜索功能)。后端是用 python 编写的(使用 aiohttp),而我使用 react.js 网站作为客户端。上传文件时,后端将其存储在文件系统中并使用其 sha256 哈希重命名。原始名称和一些其他元数据存储在它旁边(描述等)。当用户下载文件时,我使用 multipart 提供它,我希望用户使用原始名称而不是哈希来获取它,实际上my-cool-image.pnga14e0414-b84c-4d7b-b0d4-49619b9edd8a 更用户友好。但我做不到(无论我尝试什么,下载文件都会用哈希调用)。

这是我的代码:

    async def download(self, request):

        if not request.username:
            raise exceptions.Unauthorized("A valid token is needed")

        data = await request.post()
        hash = data["hash"]

        file_path = storage.get_file(hash)
        dotfile_path = storage.get_file("." + hash)
        if not os.path.exists(file_path) or not os.path.exists(dotfile_path):
            raise exceptions.NotFound("file <{}> does not exist".format(hash))
        with open(dotfile_path) as dotfile:
            dotfile_content = json.load(dotfile)
            name = dotfile_content["name"]

        headers = {
            "Content-Type": "application/octet-stream; charset=binary",
            "Content-Disposition": "attachment; filename*=UTF-8''{}".format(
                urllib.parse.quote(name, safe="")
            ),
        }

        return web.Response(body=self._file_sender(file_path), headers=headers)

这是它的样子(根据浏览器): 看起来不错,但它不起作用。

我想澄清一件事:有时我会收到一条警告(在客户端)说Resource interpreted as Document but transferred with MIME type application/octet-stream。我不知道文件的 MIME 类型,因为它们是由用户提供的,但我尝试使用 image/png(我使用存储在服务器上的 png 图像进行了测试)。该文件没有被下载(它显示在浏览器中,这不是我想要的)并且文件名仍然是它的哈希,所以它对我的问题没有帮助。

这是后端的全部源代码: https://git.io/nexmind-node 和前端: https://git.io/nexmind-client

编辑: 我收到了 Julien Castiaux 的第一个答案,所以我尝试实施它,即使它看起来更好,但它并没有解决我的问题(我仍然有完全相同的行为):


    async def download(self, request):

        if not request.username:
            raise exceptions.Unauthorized("A valid token is needed")

        data = await request.post()
        hash = data["hash"]

        file_path = storage.get_file(hash)
        dotfile_path = storage.get_file("." + hash)
        if not os.path.exists(file_path) or not os.path.exists(dotfile_path):
            raise exceptions.NotFound("file <{}> does not exist".format(hash))
        with open(dotfile_path) as dotfile:
            dotfile_content = json.load(dotfile)
            name = dotfile_content["name"]

        response = web.StreamResponse()
        response.headers['Content-Type'] = 'application/octet-stream'
        response.headers['Content-Disposition'] = "attachment; filename*=UTF-8''{}".format(
            urllib.parse.quote(name, safe="")  # replace with the filename
        )
        response.enable_chunked_encoding()
        await response.prepare(request)

        with open(file_path, 'rb') as fd:  # replace with the path
            for chunk in iter(lambda: fd.read(1024), b""):
                await response.write(chunk)
        await response.write_eof()

        return response

【问题讨论】:

    标签: python http request multipart aiohttp


    【解决方案1】:

    来自 aiohttp3 文档

    StreamResponse 用于流式传输数据,而 Response 包含 HTTP BODY 作为属性,并将自己的内容作为具有正确 Content-Length HTTP 标头的单件发送。

    在发送(可能非常大的)文件时,您宁愿使用aiohttp.web.StreamResponse。使用StreamResponse,您可以完全控制传出的 http 响应流:标头操作(包括文件名)和分块编码。

    from aiohttp import web
    import urllib.parse
    
    async def download(req):
        resp = web.StreamResponse()
        resp.headers['Content-Type'] = 'application/octet-stream'
        resp.headers['Content-Disposition'] = "attachment; filename*=UTF-8''{}".format(
            urllib.parse.quote(filename, safe="")  # replace with the filename
        )
        resp.enable_chunked_encoding()
        await resp.prepare(req)
    
        with open(path_to_the_file, 'rb') as fd:  # replace with the path
            for chunk in iter(lambda: fd.read(1024), b""):
                await resp.write(chunk)
        await resp.write_eof()
    
        return resp
    
    app = web.Application()
    app.add_routes([web.get('/', download)])
    
    web.run_app(app)
    

    希望对你有帮助!

    【讨论】:

    • 感谢您的回答。我试图实现你的代码,不幸的是我仍然有同样的问题。我将编辑我的问题以放置修改后的版本。
    • 两者之间是否有反向代理?如果有的话,能否添加相关配置?
    • 不,没有,我只是在运行 aiohttp 进行测试
    猜你喜欢
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 2015-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多