项目目录:
1.上传单个文件
思路:
(1)获取上传文件,使用 const file = ctx.request.files.file
(2)我们使用 fs.createReadStream 来读取文件流;如代码:const fileReader = fs.createReadStream(file.path);
(3)对当前上传的文件保存到 /static/upload 目录下,因此定义变量:const filePath = path.join(__dirname, '/static/upload/');
(4) 组装文件的绝对路径,代码:const fileResource = filePath + `/${file.name}`;
(5)使用 fs.createWriteStream 把该文件写进去,如代码:const writeStream = fs.createWriteStream(fileResource);
(6) 下面这段代码就是判断是否有该目录,如果没有改目录,就创建一个 /static/upload 这个目录,如果有就直接使用管道流pipe拼接文件,如代码:fileReader.pipe(writeStream);
if (!fs.existsSync(filePath)) { fs.mkdir(filePath, (err) => { if (err) { throw new Error(err); } else { fileReader.pipe(writeStream); ctx.body = { url: uploadUrl + `/${file.name}`, code: 0, message: '上传成功' }; } }); } else { fileReader.pipe(writeStream); ctx.body = { url: uploadUrl + `/${file.name}`, code: 0, message: '上传成功' }; }
最后我们使用 ctx.body 返回到页面来,因此如果我们上传成功了,就会在upload页面返回如下信息了;如下图所示:
源码:
static/upload.html
<!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>文件上传</title> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <!-- 使用form表单提交 <form action="http://localhost:3001/upload" method="post" enctype="multipart/form-data"> <div> <input type="file" name="file"> </div> <div> <input type="submit" value="提交"/> </div> </form> --> <div> <input type="file" name="file" id="file"> </div> <script type="text/javascript"> var file = document.getElementById('file'); const instance = axios.create({ withCredentials: true }); file.onchange = function(e) { var f1 = e.target.files[0]; var fdata = new FormData(); fdata.append('file', f1); instance.post('http://localhost:3001/upload', fdata).then(res => { console.log(res); }).catch(err => { console.log(err); }); } </script> </body> </html>
app.js
// 引入模块 const Koa = require('koa'); const fs = require('fs'); const path = require('path'); const router = require('koa-router')(); const koaBody = require('koa-body'); const static = require('koa-static'); // 实例化 const app = new Koa(); app.use(koaBody({ multipart: true, // 支持文件上传 formidable: { maxFieldsSize: 2 * 1024 * 1024, // 最大文件为2兆 multipart: true // 是否支持 multipart-formdate 的表单 } })); const uploadUrl = "http://localhost:3001/static/upload"; // 配置路由 router.get('/', (ctx) => { // 设置头类型, 如果不设置,会直接下载该页面 ctx.type = 'html'; // 读取文件 const pathUrl = path.join(__dirname, '/static/upload.html'); ctx.body = fs.createReadStream(pathUrl); }); // 上传文件 router.post('/upload', (ctx) => { // 获取上传文件 const file = ctx.request.files.file; console.log(file); // 读取文件流 const fileReader = fs.createReadStream(file.path); console.log(fileReader); // 设置文件保存路径 const filePath = path.join(__dirname, '/static/upload/'); // 组装成绝对路径 const fileResource = filePath + `/${file.name}`; /** * 使用 createWriteStream 写入数据,然后使用管道流pipe拼接 */ const writeStream = fs.createWriteStream(fileResource); // 判断 /static/upload 文件夹是否存在,如果不在的话就创建一个 if (!fs.existsSync(filePath)) { fs.mkdir(filePath, (err) => { if (err) { throw new Error(err); } else { fileReader.pipe(writeStream); ctx.body = { url: uploadUrl + `/${file.name}`, code: 0, message: '上传成功' }; } }); } else { fileReader.pipe(writeStream); ctx.body = { url: uploadUrl + `/${file.name}`, code: 0, message: '上传成功' }; } }); // 配置静态资源路径 app.use(static(path.join(__dirname))); // 启动路由 app.use(router.routes()).use(router.allowedMethods()); // 监听端口号 app.listen(3001, () => { console.log('server is listen in 3001'); });
2.上传多个文件
为了支持多个文件上传,和单个文件上传,我们需要把代码改下,改成如下:
static/upload.html
<!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>文件上传</title> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <!-- 使用form表单提交 <form action="http://localhost:3001/upload" method="post" enctype="multipart/form-data"> <div> <input type="file" name="file"> </div> <div> <input type="submit" value="提交"/> </div> </form> --> <!-- 上传单个文件 <div> <input type="file" name="file" id="file"> </div> <script type="text/javascript"> var file = document.getElementById('file'); const instance = axios.create({ withCredentials: true }); file.onchange = function(e) { var f1 = e.target.files[0]; var fdata = new FormData(); fdata.append('file', f1); instance.post('http://localhost:3001/upload', fdata).then(res => { console.log(res); }).catch(err => { console.log(err); }); } </script> --> <div> <input type="file" name="file" id="file" multiple="multiple"> </div> <script type="text/javascript"> var file = document.getElementById('file'); const instance = axios.create({ withCredentials: true }); file.onchange = function (e) { var files = e.target.files; var fdata = new FormData(); if (files.length > 0) { for (let i = 0; i < files.length; i++) { const f1 = files[i]; fdata.append('file', f1); } } instance.post('http://localhost:3001/upload', fdata).then(res => { console.log(res); }).catch(err => { console.log(err); }); } </script> </body> </html>
如上是多个文件上传的html代码和js代码,就是把多个数据使用formdata一次性传递多个数据过去,现在我们需要把app.js 代码改成如下了,app.js 代码改的有点多,最主要是要判断 传过来的文件是单个的还是多个的逻辑,所有代码如下:
// 引入模块 const Koa = require('koa'); const fs = require('fs'); const path = require('path'); const router = require('koa-router')(); const koaBody = require('koa-body'); const static = require('koa-static'); // 实例化 const app = new Koa(); app.use(koaBody({ multipart: true, // 支持文件上传 formidable: { maxFieldsSize: 2 * 1024 * 1024, // 最大文件为2兆 multipart: true // 是否支持 multipart-formdate 的表单 } })); const uploadUrl = "http://localhost:3001/static/upload"; router.get('/', (ctx) => { // 设置头类型, 如果不设置,会直接下载该页面 ctx.type = 'html'; // 读取文件 const pathUrl = path.join(__dirname, '/static/upload.html'); ctx.body = fs.createReadStream(pathUrl); }); /** * flag: 是否是多个文件上传 */ const uploadFilePublic = function (ctx, files, flag) { const filePath = path.join(__dirname, '/static/upload/'); let file, fileReader, fileResource, writeStream; const fileFunc = function (file) { // 读取文件流 fileReader = fs.createReadStream(file.path); // 组装成绝对路径 fileResource = filePath + `/${file.name}`; /* 使用 createWriteStream 写入数据,然后使用管道流pipe拼接 */ writeStream = fs.createWriteStream(fileResource); fileReader.pipe(writeStream); }; const returnFunc = function (flag) { console.log(flag); console.log(files); if (flag) { let url = ''; for (let i = 0; i < files.length; i++) { url += uploadUrl + `/${files[i].name},` } url = url.replace(/,$/gi, ""); ctx.body = { url: url, code: 0, message: '上传成功' }; } else { ctx.body = { url: uploadUrl + `/${files.name}`, code: 0, message: '上传成功' }; } }; if (flag) { // 多个文件上传 for (let i = 0; i < files.length; i++) { const f1 = files[i]; fileFunc(f1); } } else { fileFunc(files); } // 判断 /static/upload 文件夹是否存在,如果不在的话就创建一个 if (!fs.existsSync(filePath)) { fs.mkdir(filePath, (err) => { if (err) { throw new Error(err); } else { returnFunc(flag); } }); } else { returnFunc(flag); } } // 上传单个或多个文件 router.post('/upload', (ctx) => { let files = ctx.request.files.file; const fileArrs = []; if (files.length === undefined) { // 上传单个文件,它不是数组,只是单个的对象 uploadFilePublic(ctx, files, false); } else { uploadFilePublic(ctx, files, true); } }); // 配置静态资源路径 app.use(static(path.join(__dirname))); // 启动路由 app.use(router.routes()).use(router.allowedMethods()); // 监听端口号 app.listen(3001, () => { console.log('server is listen in 3001'); });
然后我现在来演示下,当我选择多个文件,比如现在选择两个文件,会返回如下数据:
当我现在只选择一个文件的时候,只会返回一个文件,如下图所示:
如上app.js改成之后的代码现在支持单个或多个文件上传了。
转自:https://www.cnblogs.com/tugenhua0707/p/10828869.html
.