上传文件是网页开发人员最常见的任务之一。使用PHP或其他服务器端框架时,这是一个相当简单的操作。然而,使用Node.js时并不那么简单,除非你了解如何使用Buffer API读取二进制文件流(即将文件内容作为二进制代码上传)。幸运的是,Node.js社区提供了几种解决方案。
项目设置
我们在客户端使用React.js,Express.js作为中间(协调)层和外部REST API,这不是React/Express体系结构的一部分。处理文件的一种方法是直接通过浏览器将它们上传到服务器,但这在每种情况下可能都不是理想的,例如,当API位于不同的域或者你想在发送到API之前修改文件时。这种情况下,Node服务器是一个完美的解决方案。然而,正如我之前提到的,开发人员必须在这里跳过一些障碍。接下来我们看看如何做到这一点。
客户端
这是一个带有事件处理程序的简单输入字段:
<input type="file" onChange={e => this.props.uploadFile(e.target.files[0], e.target.files[0].name)} />
让我们看看客户端发生了什么。当输入改变(文件被添加)时,事件处理程序将触发Redux action creator并将文件和名称作为参数传递。操作创建者将构建FormData对象,这是处理multipart/form-data
所必需的。在我们将文件和名称附加到表单的数据对象之后,它就可以传递到服务器了。Axios是一个很好的HTTP请求库。在这种情况下,我们将数据POST到Node.js路由。让我们看看服务器上发生了什么。
服务器
一旦客户端提供了文件和与文件相关的其他元信息,服务器就必须知道如何处理二进制文件。几个库可以帮助解决这个问题。我们将使用Express.js团队维护的Multer。
让我们看看服务器上发生了什么。首先,我们配置Multer使用本地/files/
目录存储来自客户端上传的文件。在我们将其发送到REST API之前,将文件保存在某个地方非常重要,因为在大多数情况下,我们将不得不提供完整的文件路径。另一个选择是将其保存在内存中,但这可能会导致服务器崩溃。接下来,我们要确保生成具有文件扩展名的唯一文件名(默认情况下不提供)。
下一步是创建实际的路由,客户端发送FormData,并且我们传递Multer中间件。当路由接收到文件时,它首先通过中间件并使用新生成的文件名存储在我们的/files目录中。所以,当我们到达回调(可以重构为自定义中间件)时,文件可用作req对象的一部分。从这里开始,我们可以做任何需要做的事情,在这种情况下,调用我们的外部API(可以是S3或任何处理文件的API)并传递文件和元信息。
2017年6月25日更新。使用流直接上传到cloudinary CDN。
上传文件的另一种方法是使用Node.js流而不是在将其发送到CDN之前存储临时文件。下面是中间件的样子。
const cloudinary = require('cloudinary');
const multer = require('multer');
const stream = require('stream');
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
cloudinary.config({
cloud_name: 'your_cloud_name',
api_key: 'your_api_key',
api_secret: 'your_api_secret'
});
module.exports = app => {
app.post('/upload', upload.single('file'), (req, res) => {
const stream = cloudinary.uploader.upload_stream(result => {
console.log(result);
res.send(result);
});
streamifier.createReadStream(req.file.buffer).pipe(stream);
});
};
使用更新的cloudinary API创建中间件
一旦有了这个处理流将文件流式传输到CDN的中间件,就可以添加新路由来触发它。
现在你可以从React(或任何其他客户端框架)上传文件了。这是一个例子:
axios.post('/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
.then(response => console.log(response))
.catch(err => console.log(err));
结论
这可能看起来像一个对一个常见问题的过度工程化的解决方案。然而,在将文件发送到另一个来源之前,将需要使用节点服务器的情况。当文件上传不是表单提交方法的一部分,并且需要独立处理文件上传时,这将特别棘手。
评论(0)