FastAPIでファイルダウンロードさせる時の Content-Disposition ヘッダーの公開方法
ファイルダウンロードを実装する際に、レスポンスのヘッダー情報から filename を取得する必要があるのだが、FastAPIの場合は、デフォルトでは Content-Disposition ヘッダーを返却しません。
デフォルトで公開される CORS セーフリストレスポンスヘッダーは 7 つだけ
CORS セーフリストレスポンスヘッダー
レスポンスヘッダー |
---|
Cache-Control |
Content-Language |
Content-Length |
Content-Type |
Expires |
Last-Modified |
Pragma |
FastAPIで、 Access-Control-Expose-Headers に Content-Disposition ヘッダーの公開を許可する
FastAPI アプリケーションでは CORSMiddleware を使用して、CORSに関する設定ができます。
Access-Control-Expose-Headers レスポンスヘッダーで、レスポンスの一部としてどのヘッダーを公開するかを、その名前を列挙する必要があります。
CORSMiddlewareで、Access-Control-Expose-Headersを設定するには、
expose_headers に、公開したいレスポンスヘッダーを列挙します。デフォルトは [] です。
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http://localhost",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["Content-Disposition"] # ここ!
)
FileResponse をレスポンスする
from fastapi import FastAPI
from fastapi.responses import FileResponse
file_name = "見積書20221206124057.xlsx"
file_path = "/tmp/見積書20221206124057.xlsx"
app = FastAPI()
@app.get("/")
async def main():
return FileResponse(
path = file_path,
filename = file_name
)
レスポンスヘッダーとして、公開される
axiosとfile-saverで、ダウンロードして保存させる例
import axios from 'axios';
import saveAs from 'file-saver';
function authHeaders(token, config = {}) {
config.headers = {
Authorization: `Bearer ${token}`,
};
return config;
}
function getFileName(contentDisposition) {
let fileName = contentDisposition.substring(contentDisposition.indexOf("''") + 2,
contentDisposition.length
);
//デコードするとスペースが"+"になるのでスペースへ置換します
fileName = decodeURI(fileName).replace(/\+/g, " ");
return fileName;
}
const instance = axios.create({
baseURL: process.env.VUE_APP_API_URL,
});
/**
* 見積エクセルダウンロード
* @param {*} token
* @param {*} id
* @returns response
*/
var id = 1;
instance.get(`/downloads/quotation/excel/${id}`, authHeaders(token, {
responseType: 'blob'
})).then(response => {
const blob = new Blob([response.data], {
type: response.data.type
});
//レスポンスヘッダからファイル名を取得します
const contentDisposition = response.headers["Content-Disposition"];
const fileName = getFileName(contentDisposition)
//ダウンロードします
saveAs(blob, fileName);
});