React 中的异步文件上传
介绍
上传文件是实际应用程序的常见要求。更可能的情况是,它只是应用程序的一个次要功能,而不是核心组件。因此,我们往往会忽视正确设计它的重要性。懒惰的文件上传器和文件服务器肯定会给未来带来麻烦。目前,我正在努力在庞大的组件结构中重新实现一个同样糟糕的实现。因此,在本指南中,我将探讨文件上传的最佳实践及其前端方面。
让我们看一下以下场景:我们的应用程序中有一个简单的表单,它由文本输入、文件上传和文本区域组成。我们正在构建的应用程序应该能够异步将表单数据提交给 REST API。首先,我们将概述基本的 App 组件以及向 API 提交数据的先决条件。我为 React 使用的 HTTP 客户端是axios,但您也可以使用fetch。使用以下命令初始化前端并修改 App.js 以包含我们的上传器样板。如果您想快速浏览,可以在Github repo中找到源代码。
$ create-react-app uploader
$ cd uploader
$ npm install --save axios
// App.js
import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
const API_BASE = "http://localhost:5000"
function submitForm(contentType, data, setResponse) {
axios({
url: `${API_BASE}/upload`,
method: 'POST',
data: data,
headers: {
'Content-Type': contentType
}
}).then((response) => {
setResponse(response.data);
}).catch((error) => {
setResponse("error");
})
}
function App() {
const [title, setTitle] = useState("");
const [file, setFile] = useState(null);
const [desc, setDesc] = useState("");
function uploadWithFormData(){
}
function uploadWithJSON(){
}
return (
<div className="App">
<h2>Upload Form</h2>
<form>
<label>
File Title
<input type="text" vaue={title}
onChange={(e) => { setTitle(e.target.value )}}
placeholder="Give a title to your upload" />
</label>
<label>
File
<input type="file" name="file" onChange={(e) => setFile(e.target.files[0])} />
</label>
<label>
Description
<textarea value={desc} onChange={(e) => setDesc(e.target.value)}></textarea>
</label>
<input type="button" value="Upload as Form" onClick={uploadWithFormData} />
<input type="button" value="Upload as JSON" onClick={uploadWithJSON}/>
</form>
</div>
);
}
export default App;
文件服务器
在大多数情况下,拥有文件服务器只是应用程序的次要问题。因此,我们倾向于懒惰地实现它,而没有适当考虑后果。由于本指南的目的不是关注文件服务器的后端方面,因此我只会强调一些有用的提示。
确保文件上传功能不会阻塞应用程序的其余部分。
使用单独的服务器后端来处理文件上传可能有助于后期扩展。Node.Js 文件上传是这里的首选做法。
请注意从服务器实例提供文件时的带宽要求。如果您的文件较大,使用 CDN 服务会更高效。
检查您的平台提供商是否有对象存储服务 - AWS、GCloud、DigitalOCean 都有自己的对象存储提供商。利用这些服务可带来额外的好处,包括文件的高可用性和节省服务器带宽。
回到主题,在本指南中,我编译了一个简单的 node.js 文件服务器。源代码可在 Github Repo 中找到。此服务器后端公开了一个具有两个端点的 REST API:
/hello将仅显示臭名昭著的 Hello world!消息。
/upload将接受 JSON 数据或表单数据的键值。
由于上传端点支持两种内容类型,我们可以使用它来测试从前端促进文件上传的不同方法。现在我们可以探索与 React 一起使用的不同上传机制。
使用 FormData 上传
与任何单页应用程序 (SPA) 一样,在 React 中我们倾向于通过 REST 端点与服务器交互。因此,我们最终将整个消息传递结构基于通用数据格式,在几乎所有情况下都是 JSON。但是,一旦我们需要实现文件上传,我们很快就会意识到 JSON 可能不是该作业的默认候选者。通常,文件以多部分表单数据的形式发送到服务器。它的行为方式消除了前端开发人员对大文件进行分块和流式传输的不必要复杂性。虽然它缺乏控制,但如果整个应用程序不基于文件上传的功能,它仍然是上传的首选方式。所以,让我们首先弄清楚如何将文件作为表单数据发送。
幸运的是,Javascript API 附带FormData API 来动态构建表单。在下面的代码中,我们使用表单数据完成文件上传过程。
function uploadWithFormData(){
const formData = new FormData();
formData.append("title", title);
formData.append("file", file);
formData.append("desc", desc);
submitForm("multipart/form-data", formData, (msg) => console.log(msg));
}
代码非常简单。我们首先创建一个 FormData 实例,然后将字段附加到其中。唯一需要记住的是,文件的字段名称应与服务器端的相应字段名称匹配。插入并测试后,您应该会看到在服务器文件夹中创建了一个文件,控制台显示消息“OKAY”。
优点
- 文件上传机制非常简单,浏览器 API 可以处理上传的复杂性。
请注意,这仍然不是一个合适的流式传输解决方案,因为整个文件是在一次 POST 请求中传输的。对于非常大的文件,需要实现自定义的切片和上传机制。
- 许多外部 API 将能够默认支持它。例如,如果您使用第三方文件存储服务,他们的 API 将支持使用上述方法而无需进行任何调整。
缺点
如果您已经拥有定义良好的 JSON API,处理表单数据会很麻烦。首先,您需要确保服务器支持它;然后,您需要在提交之前在前端进行更改以将数据字典转换为表单数据。因此,在下一节中,我们将探讨如何为现有的基于 JSON 的 API 上传。
使用 JSON 上传
如果我们要使用 JSON 上传,唯一的要求就是文件应该以某种方式转换为字符串。如果我们可以将文件表示为字符串,我们也可以在 JSON 主体中传输它。对于 blob(或二进制对象),有几种不同的字符串表示形式。对于我们的用例,我们将使用Base64 编码。Base64 本质上将二进制数组转换为 ASCII 字符串格式。在下面的代码中,我们实现了这一点。
async function uploadWithJSON(){
const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
const data = {
title: title,
file: await toBase64(file),
desc: desc
}
submitForm("application/json", data, (msg) => console.log(msg));
}
我们使用FileReader类将文件转换为 Base64,然后像普通 JSON 消息一样将其提交到服务器。
优点
这种方法的主要优势是可以使用相同的 JSON API,而无需对后端和前端进行重大更改。不过,正如我上面所说,我仅建议将此方法用于较小的文件。例如,这对于个人资料头像上传来说是理想的选择,其中输入图像经过处理,并且后端仅保存低质量的裁剪图像。
结论
在本指南中,我们探讨了在 React 中实现异步文件上传的不同方法。首先,我们研究了一种基于 FormData 的方法,我们模拟 HTML 表单请求并使用 Axios 发送它。然后,我们观察了如何使用 Base64 编码来提供文件上传功能,而无需更改现有 API。一个重要的收获是要知道每种方法都有其优点和缺点。如果您的应用程序在某个时候需要文件上传,我建议在初始设计时牢记这一点。这可能会在以后为您节省数天的工作时间。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~