본문 바로가기

개발하자

FastAPI를 사용하여 대용량 파일을 다운로드하는 방법은 무엇입니까?

반응형

FastAPI를 사용하여 대용량 파일을 다운로드하는 방법은 무엇입니까?

Fast에서 대용량 파일()을 다운로드하려고 합니다API 백엔드. 서버 측에서는 StackOverflow에 대한 많은 관련 질문에서 보았던 것처럼 파일 경로를 확인한 다음 전체 파일을 반환하는 데 사용합니다.

서버 측:

return FileResponse(path=file_name, media_type='application/octet-stream', filename=file_name)

그 후 다음 오류가 발생합니다:

  File "/usr/local/lib/python3.10/dist-packages/fastapi/routing.py", line 149, in serialize_response
    return jsonable_encoder(response_content)
  File "/usr/local/lib/python3.10/dist-packages/fastapi/encoders.py", line 130, in jsonable_encoder
    return ENCODERS_BY_TYPE[type(obj)](obj)
  File "pydantic/json.py", line 52, in pydantic.json.lambda
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

나도 사용해봤지만, 같은 오류가 발생했다. 다른 방법은 없나요?

내 코드:

@x.post("/download")
async def download(file_name=Body(), token: str | None = Header(default=None)):
    file_name = file_name["file_name"]
    # should be something like xx.tar
    def iterfile():
        with open(file_name,"rb") as f:
            yield from f
    return StreamingResponse(iterfile(),media_type='application/octet-stream')

네, 여기 이 문제에 대한 업데이트가 있습니다. 나는 이 api에서 오류가 발생한 것이 아니라 api가 이것의 전달 요청을 하고 있다는 것을 발견했다.

@("/")
def f():
    req = requests.post(url ="/download")
    return req.content

그리고 여기서 파일을 반환하면 인코딩 문제가 발생했습니다.

요청을 사용할 때는 동일한 미디어 유형을 설정해야 합니다. 여기 있습니다. 효과가 있어요!




예를 들어, 다음과 같은 경우 속도가 다소 느릴 수 있습니다:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

some_file_path = 'large-video-file.mp4'
app = FastAPI()

@app.get('/')
def main():
    def iterfile():
        with open(some_file_path, mode='rb') as f:
            yield from f

    return StreamingResponse(iterfile(), media_type='video/mp4')

대신 지정된 청크 크기를 사용하여 파일을 읽을 수 있는 생성기를 만들 수 있으므로 프로세스 속도가 빨라집니다. 아래에서 예를 찾을 수 있습니다.

는 제너레이터 또는 일반 제너레이터/반복자를 사용하여 응답 본문을 스트리밍할 수 있습니다. /를 지원하지 않는 표준 메서드를 사용한 경우 정상적으로 제너레이터 기능을 선언해야 합니다. 그러나 FastAPI/Starlette는 전달한 제너레이터가 비동기인지 여부를 확인하기 때문에 비동기적으로 작동하며, 그렇지 않으면 별도의 스레드에서 생성기를 실행하여 t를 사용합니다모자가 기다리고 있다.

응답에 헤더를 설정하여 콘텐츠가 예상되는 것(예: 비디오, 오디오 파일 등)인지, 로컬로 저장되는 것(지정된 사용)인지 여부를 나타낼 수 있습니다.

(MIME 유형이라고도 함)의 경우 두 가지 기본 MIME 유형이 있습니다( 참조):

  • 텍스트 파일의 기본값입니다. 텍스트 파일은 사람이 읽을 수 있어야 하며 이진 데이터를 포함해서는 안 된다.
  • 다른 모든 경우에 대한 기본값입니다. 안.

질문에 나와 있는 것처럼 확장자가 있는 파일의 경우 와 다른 형식을 사용할 수도 있습니다. 그렇지 않으면 파일의 형식을 알 수 없는 경우 를 참조하십시오. 일반적인 MIME 유형 목록은 위의 링크된 설명서를 참조하십시오.

옵션 1 - 일반 제너레이터 사용

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

CHUNK_SIZE = 1024 * 1024  # = 1MB - adjust the chunk size as desired
some_file_path = 'large_file.tar'
app = FastAPI()

@app.get('/')
def main():
    def iterfile():
        with open(some_file_path, 'rb') as f:
            while chunk := f.read(CHUNK_SIZE):
                yield chunk

    headers = {'Content-Disposition': 'attachment; filename="large_file.tar"'}
    return StreamingResponse(iterfile(), headers=headers, media_type='application/x-tar')

옵션 2 - 제너레이터 사용

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import aiofiles

CHUNK_SIZE = 1024 * 1024  # = 1MB - adjust the chunk size as desired
some_file_path = 'large_file.tar'
app = FastAPI()

@app.get('/')
async def main():
    async def iterfile():
       async with aiofiles.open(some_file_path, 'rb') as f:
            while chunk := await f.read(CHUNK_SIZE):
                yield chunk

    headers = {'Content-Disposition': 'attachment; filename="large_file.tar"'}
    return StreamingResponse(iterfile(), headers=headers, media_type='application/x-tar')

반응형