表单提交上的 Django 动态文件输出在移动设备上不起作用

Django dynamic file output on form submit doesn't work on mobile

我正在尝试通过写入 io.BytesIO 来输出文件,并在表单作为文件附件提交时发送。 Linux 上的 Firefox 一切正常,但移动浏览器在浏览器接受下载时发送 GET 请求,并将 HTTP 响应保存为下载的文件。

这是我的视图函数:

from django.views.decorators.csrf import csrf_exempt
from django.http import FileResponse, HttpResponse
import io


@csrf_exempt
def download_file(request):
    if request.method == 'POST':
        buffer = io.BytesIO()
        buffer.write('Text file content'.encode('UTF-8'))
        buffer.seek(0)
        return FileResponse(buffer, as_attachment=True, filename='file.txt')
    return HttpResponse('<form method="post"><button>Download</button></form>')

这是在 Linux 上从 Firefox 提交表单时日志的样子:

[20/Sep/2020 18:15:31] "POST /test/ HTTP/1.1" 200 17

Linux 上的下载文件:

Text file content

这是在 Android 上从 Firefox 提交表单时日志的样子:

[20/Sep/2020 18:16:47] "POST /test/ HTTP/1.1" 200 17
[20/Sep/2020 18:16:48] "GET /test/ HTTP/1.1" 200 52

Android 上的下载文件:

<form method="post"><button>Download</button></form>

我正在使用 Python 3.8.5 和 Django 3.1.1。

解决方案

考虑使用具有适当内容类型和内容配置的 HttpResponse 或 StreamingHttpResponse。

例子

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
import io

@csrf_exempt
def download_file(request):
    if request.method == 'POST':
        buffer = io.BytesIO()
        buffer.write('Text file content'.encode('UTF-8'))
        buffer.seek(0)
        response = HttpResponse(buffer, content_type='text/plain')
        response['Content-Disposition'] = 'attachment; filename=file.txt'

        return response

    return HttpResponse('<form method="post"><button>Download</button></form>')

参考资料

StreamingHttpResponse:https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.StreamingHttpResponse

问题

在 Firefox android 应用程序中,应用程序在 确认下载 操作后发送 HTTP GET 请求。您可以看到相同的服务器日志(在 OP 中也是如此)

解决方法是什么?

不推荐使用 HTTP POST 方法下载文件,请改用 HTTP GET(参考:)。因此,相应地更改您的视图以下载文件。

def sample_view(request):
    buffer = io.BytesIO()
    buffer.write('Text file content'.encode('UTF-8'))
    buffer.seek(0)
    return FileResponse(buffer, as_attachment=True, filename='file.txt')