如何使用ReactJS和Axios在前端和FastAPI在后端下载文件?

r1zhe5dt  于 2023-06-05  发布在  iOS
关注(0)|答案(2)|浏览(603)

我正在尝试创建一个docx文件并将其发送到前端客户端应用程序,以便可以将其下载到用户的本地计算机。我使用FastAPI作为后端。我也使用python-docx库来创建Document
下面的代码用于创建docx文件并将其保存到服务器。

@app.post("/create_file")
async def create_file(data: Item):
    document = Document()
    document.add_heading("file generated", level=1)
    document.add_paragraph("test")
    document.save('generated_file.docx')
    return {"status":"Done!"}

然后使用下面的代码将创建的docx文件作为FileResponse发送到客户端。

@app.get("/generated_file")
async def download_generated_file():
    file_path = "generated_file.docx"
    return FileResponse(file_path, media_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document', filename=file_path)

在客户端(我使用的是ReactJS):

createFile = async () => {
   const data = {
      start: this.state.start,
      end: this.state.end,
      text: this.state.text,
   };
   await axios.post("http://localhost:8000/create_file", data).then(() => {
      console.log("processing completed!");
   });
};

downloadFile = async () => {
   await axios.get("http://localhost:8000/generated_file").then((res) => {
      const url = URL.createObjectURL(new Blob([res.data]));
      const link = document.createElement("a");
      link.href = url;
      link.setAttribute("download", "generated.txt");
      link.click();
   });
};

当调用downloadFile函数时,generated.docx文件被下载。但是,docx文件始终损坏,无法打开。我尝试使用txt文件,它工作正常。我需要使用docx文件,怎么办?

xjreopfe

xjreopfe1#

在Axios GET请求中,必须确保responseType参数设置为blob。从API中获取response后,需要将Blob对象(即response.data)传递给URL.createObjectURL()函数。下面是一个完整的工作示例,说明如何在前端使用Axios或Fetch API创建和下载文件(Document)。这个答案使用了thisthis答案中的方法和代码摘录,以及herehere答案。请参阅上述答案,了解以下所用方法的详细信息。出于演示目的,下面的示例使用Jinja2Templates,但是,以类似的方式,您可以在ReactJS应用中使用下面的脚本。

app.py

from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import FileResponse
from docx import Document

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get('/')
def main(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})
    
@app.post("/create")
def create_file():
    document = Document()
    document.add_heading("file generated", level=1)
    document.add_paragraph("test")
    document.save('generated_file.docx')
    return {"status":"Done!"}
    
@app.get("/download")
def download_generated_file():
    file_path = "generated_file.docx"
    return FileResponse(file_path, media_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document', filename=file_path)

使用Axios

tempaltes/index.htnl

<!DOCTYPE html>
<html>
   <head>
      <title>Create and Download a Document</title>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>
   </head>
   <body>
      <input type="button" value="Create Document" onclick="createFile()">
      <div id="response"></div><br>
      <input type="button" value="Download Document " onclick="downloadFile()">
      <script>
         function createFile() {
            axios.post('/create', {
                  headers: {
                     'Accept': 'application/json',
                     'Content-Type': 'application/json'
                  }
               })
               .then(response => {
                  document.getElementById("response").innerHTML = JSON.stringify(response.data);
               })
               .catch(error => {
                  console.error(error);
               });
         }
         
         function downloadFile() {
            axios.get('/download', {
                  responseType: 'blob'
               })
               .then(response => {
                  const disposition = response.headers['content-disposition'];
                  filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];
                  if (filename.toLowerCase().startsWith("utf-8''"))
                     filename = decodeURIComponent(filename.replace("utf-8''", ''));
                  else
                     filename = filename.replace(/['"]/g, '');
                  return response.data;
               })
               .then(blob => {
                  var url = window.URL.createObjectURL(blob);
                  var a = document.createElement('a');
                  a.href = url;
                  a.download = filename;
                  document.body.appendChild(a); // append the element to the dom
                  a.click();
                  a.remove(); // afterwards, remove the element  
               })
               .catch(error => {
                  console.error(error);
               });
         }
      </script>
   </body>
</html>

使用Fetch API

tempaltes/index.htnl

<!DOCTYPE html>
<html>
   <head>
      <title>Create and Download a Document</title>
   </head>
   <body>
      <input type="button" value="Create Document" onclick="createFile()">
      <div id="response"></div><br>
      <input type="button" value="Download Document" onclick="downloadFile()">
      <script>
         function createFile() {
            fetch('/create', {
                  method: 'POST',
                  headers: {
                     'Accept': 'application/json',
                     'Content-Type': 'application/json'
                  }
               })
               .then(response => response.text())
               .then(data => {
                  document.getElementById("response").innerHTML = data;
               })
               .catch(error => {
                  console.error(error);
               });
         }
         
         function downloadFile() {
            fetch('/download')
               .then(response => {
                  const disposition = response.headers.get('Content-Disposition');
                  filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];
                  if (filename.toLowerCase().startsWith("utf-8''"))
                     filename = decodeURIComponent(filename.replace("utf-8''", ''));
                  else
                     filename = filename.replace(/['"]/g, '');
                  return response.blob();
               })
               .then(blob => {
                  var url = window.URL.createObjectURL(blob);
                  var a = document.createElement('a');
                  a.href = url;
                  a.download = filename;
                  document.body.appendChild(a); // append the element to the dom
                  a.click();
                  a.remove(); // afterwards, remove the element
               })
               .catch(error => {
                  console.error(error);
               });
         }
      </script>
   </body>
</html>
3npbholx

3npbholx2#

在JavaScript中,一般使用Axios库下载文件(包括React.js/Vue.js/Angular...),可以将responseType设置为blob,在Axios响应中处理文件下载。
下面是一个如何实现此目标的示例:

import axios from 'axios';

// Function to download the file
async function downloadFile() {
  try {
    const response = await axios.post('your-api-endpoint', {
      // Request payload data if needed
    }, {
      responseType: 'blob', // Set the response type to 'blob'
    });

    // Extract the filename from the Content-Disposition header
    const contentDisposition = response.headers['content-disposition'];
    const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
    const matches = filenameRegex.exec(contentDisposition);
    const filename = matches !== null && matches[1] ? matches[1].replace(/['"]/g, '') : 'download';

    // Create a URL object from the response data
    const url = window.URL.createObjectURL(new Blob([response.data]));

    // Create a temporary link element
    const link = document.createElement('a');
    link.href = url;
    link.setAttribute('download', filename); // Set the filename obtained from the response

    // Append the link to the DOM and trigger the download
    document.body.appendChild(link);
    link.click();

    // Clean up the URL object and remove the link from the DOM
    window.URL.revokeObjectURL(url);
    document.body.removeChild(link);
  } catch (error) {
    console.error('Error downloading file:', error);
  }
}

// Call the downloadFile function to initiate the file download
downloadFile();

在示例代码中:

  • 确保将your-api-endpoint替换为返回文件的API端点的实际URL。
  • 在Axios请求配置中,将responseType设置为blob,以指示您希望在响应中显示二进制文件。
  • 收到响应后,使用**window.URL.createObjectURL(new Blob([ www.example.com ]))**从响应数据创建URL对象response.data。
  • 创建一个临时链接元素并将href属性设置为创建的URL。
    *contentDisposition变量从响应中提取Content-Disposition头的值。
    *filenameRegex正则表达式用于匹配和提取头值中的文件名。
  • 然后将提取的文件名用作link元素的download属性的值:link.setAttribute('download ',filename).
  • 如果无法从响应头获取文件名,则默认为download
  • 将链接追加到DOM,调用**link.click()**触发下载。
  • 下载启动后,清理URL对象,使用**window.URL.revokeObjectURL(url)document.body.removeChild(link)**从DOM中删除link元素。
  • 如果在这个过程中出现错误,它会被catch块捕获。

注意:本示例假设API端点正确返回响应正文中的文件数据。根据您的特定用例调整API端点和有效负载。

相关问题