【问题标题】:Serving .json file to download服务 .json 文件下载
【发布时间】:2016-05-18 11:18:05
【问题描述】:

我正在尝试通过此函数提供 .json 文件。问题是每次我发出请求时,浏览器都会显示内容而不是下载文件。

我认为这可能是因为我使用.read() 作为 HttpResponse 对象构造函数的参数。但是,如果我只使用文件对象,我会得到以下异常:

TypeError: cannot serialize '_io.BufferedRandom' object

代码

try:
    invoices = models.Invoice.objects.filter(pk__in=document_ids).order_by(*ordering)
    pcustomers = models.CustomerProxy.objects.all()
    mixed_query = list(invoices) + list(pcustomers)

    file = tempfile.NamedTemporaryFile(suffix='.json')
    file.write(serializers.serialize('json', mixed_query).encode())
    file.seek(0)

    response = HttpResponse(file.read(), content_type='application/json')
    response['Content-Disposition'] = 'attachment; filename=%s' % file.name
    response['Content-Length'] = os.path.getsize(file.name)

except Exception:
    raise

return response

【问题讨论】:

    标签: python django


    【解决方案1】:

    您不需要经历整个文件生成过程来创建可下载文件,您只需正常添加 Content-Disposition 标头即可。下面的代码能用吗?

    ...
    mixed_query = list(invoices) + list(pcustomers)
    json_str = serializers.serialize('json', mixed_query))
    response = HttpResponse(json_str, content_type='application/json')
    response['Content-Disposition'] = 'attachment; filename=export.json'
    

    【讨论】:

    • 文件生成了,在/tmp下可以看到。我只是无法将其作为可下载文件而不是内容提供。
    • 你不需要经历整个文件生成过程来创建一个可下载的文件,你只需要添加 Content-Disposition 头。如果你简化代码,像我在上面的代码中那样删除整个文件创建部分,它是如何工作的?
    • 如果我这样做,我会得到和以前一样的结果,也就是显示为 json 的页面。
    • 我在家里尝试过类似的代码,并得到了附件。我怀疑您的浏览器可能与 JSON 文件类型相关联。您可以尝试 (a) 与另一个浏览器 (b) 与另一个 content_type (例如 text/plain 或 application/x-invoices)
    • 我有一个封装请求并替换标头的函数。你是对的。经过很多小时试图找出问题所在,我错过了这个小细节。谢谢。
    【解决方案2】:

    根据您显示的代码,您不需要写入临时文件。为什么不直接将serialize() 的结果传递给HttpResponse()

    response = HttpResponse(serializers.serialize('json', mixed_query), content_type='application/json')
    

    您可以将附件名称设置为您喜欢的任何名称,描述性的名称似乎比tempfile.NamedTemporaryFile() 生成的随机字母数字字符串更好。

    response['Content-Disposition'] = 'attachment; filename="invoices_and_customers.json"'
    

    如果真的要指定长度:

    response['Content-Length'] = len(response.content)
    

    或者您可以将 ConditionalGetMiddleware 中间件添加到您的设置中,并让 Django 为您添加 Content-Length

    【讨论】:

    • 没错。我们正在使用一个函数来封装请求,最后它正在替换标头。我会给他正确的答案,因为一旦他是初学者,他可能会渴望获得更多积分:p。谢谢!
    • 很公平。 @raphv 也最先回答。
    【解决方案3】:

    将此添加到您的 Http 响应中

    HttpResponse(mimetype='application/force-download')
    

    【讨论】:

    • 它不起作用。我正在使用 Python 3.5,它说该构造函数没有 mimetype 属性。也许如果你的意思是content_type="application/force-download",我试过了,它也是。
    猜你喜欢
    • 1970-01-01
    • 2021-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    • 2021-09-28
    • 2020-12-26
    相关资源
    最近更新 更多