面试官:如何实现文件上传?说说你的思路
一、什么是文件上传?
文件上传在日常开发中应用很广泛,比如在发微博、发微信朋友圈等操作中都会用到图片上传功能。由于浏览器的安全限制,浏览器不能直接操作文件系统,因此需要通过浏览器暴露出来的统一接口来访问文件,然后将文件内容读取到指定的内存中,并执行提交请求操作,将内存中的文件内容上传到服务端,服务端再解析前端传来的数据信息,最终将文件保存到服务器的文件系统中。
文件上传需要设置请求头为content-type: multipart/form-data
,其中multipart
表示互联网上的混合资源,即资源由多种元素组成,而form-data
表示可以使用HTML Forms和POST方法上传文件。
文件上传的请求结构如下:
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=--------------------------1234567890
--------------------------1234567890
Content-Disposition: form-data; name="file"; filename="example.jpg"
Content-Type: image/jpeg
... binary data of the image ...
--------------------------1234567890--
其中,boundary
表示分隔符,用于分隔不同的表单项。每个表单项必须包含Content-Disposition
头,其他头信息如Content-Type
为可选项。
二、如何实现文件上传?
文件上传可以分为两个步骤:
- 文件的上传
- 文件的解析
1. 文件上传
在前端,我们可以通过HTML的<input type="file">
元素来实现文件上传的表单:
<form action="http://localhost:8080/api/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="file" value="" multiple="multiple" />
<input type="submit" value="提交"/>
</form>
在后端,我们可以使用Koa框架中的koa-body
中间件来解析上传的文件数据:
npm install koa-body
const Koa = require('koa');
const koaBody = require('koa-body');
const app = new Koa();
app.use(koaBody({
multipart: true,
formidable: {
maxFileSize: 200 * 1024 * 1024 // 设置上传文件大小最大限制,默认2M
}
}));
app.use(async (ctx) => {
// 获取上传的文件
const file = ctx.request.files.file;
// 创建可读流
const reader = fs.createReadStream(file.path);
let filePath = path.join(__dirname, 'public/upload/') + `/${
file.name}`;
// 创建可写流
const upStream = fs.createWriteStream(filePath);
// 可读流通过管道写入可写流
reader.pipe(upStream);
ctx.body = "上传成功!";
});
app.listen(8080, () => {
console.log('Server started on http://localhost:8080');
});
2. 文件解析
在服务器端,我们需要将上传的文件进行解析,保存到指定的目录中。在上述代码中,我们通过使用fs
模块来处理文件的保存操作。
除了使用koa-body
,我们还可以使用koa-multer
中间件来实现文件上传:
npm install koa-multer
const Koa = require('koa');
const Router = require('koa-router');
const multer = require('koa-multer');
const path = require('path');
const app = new Koa();
const router = new Router();
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "./upload/")
},
filename: (req, file, cb) => {
cb(null, Date.now() + path.extname(file.originalname))
}
})
const upload = multer({
storage
});
router.post("/upload", upload.single('file'), (ctx, next) => {
console.log(ctx.req.file); // 获取上传文件
})
app.use(router.routes());
app.listen(8080, () => {
console.log('Server started on http://localhost:8080');
});
以上代码中,我们使用multer
中间件来实现文件上传,storage
选项用于设置文件的保存路径和文件名,upload.single('file')
表示只上传单个文件。