Невозможно получить правильное имя файла, используя составные заголовки - PullRequest
0 голосов
/ 24 апреля 2020

Я работаю над своего рода персональным облачным проектом, который позволяет загружать и скачивать файлы (с функцией поиска, похожей на Google). Серверная часть написана на python (с использованием aiohttp), а я использую веб-сайт реакции. js в качестве клиента. Когда файл загружен, сервер сохраняет его в файловой системе и переименовывает его в sha256 га sh. Оригинальное имя и несколько других метаданных хранятся рядом с ним (описание и т. Д. c). Когда пользователь загружает файл, я передаю его, используя multipart, и я хочу, чтобы пользователь получил его с оригинальным именем, а не с ha sh, действительно, my-cool-image.png более удобен для пользователя, чем a14e0414-b84c-4d7b-b0d4-49619b9edd8a. Но я не могу этого сделать (что бы я ни пытался, файл загрузки вызывается с ха sh).

Вот мой код:

    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)

Вот что это выглядит (согласно браузеру): request seen from browser Кажется правильным, но это не работает.

Одна вещь, которую я хочу прояснить, хотя: иногда я получаю предупреждение (о на стороне клиента) говоря Resource interpreted as Document but transferred with MIME type application/octet-stream. Я не знаю MIME-тип файлов, поскольку они предоставляются пользователями, но я попытался использовать image / png (я сделал свой тест с изображением png, хранящимся на сервере). Файл не был загружен (он отображался в браузере, а это не то, что мне нужно), а имя файла все еще было ha sh, поэтому это не помогло с моей проблемой.

Вот все исходные коды бэкэнда: https://git.io/nexmind-node И внешнего интерфейса: https://git.io/nexmind-client

РЕДАКТИРОВАТЬ: я получил первый ответ от Жюльена Кастио, так Я попытался реализовать его, хотя он и выглядит лучше, но он не решает мою проблему (у меня все еще такое же поведение):


    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

1 Ответ

0 голосов
/ 24 апреля 2020

Из документации aiohttp3

StreamResponse предназначен для потоковой передачи данных, в то время как Response содержит HTTP BODY в качестве атрибута и отправляет собственный контент как единое целое с правильным HTTP-заголовком Content-Length.

Вы скорее используете 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)

Надеюсь, это поможет!

...