提示:koa踩坑路之–分片上传图片,每次存储切片地址,待全部切片上传完成后生成图片
前言
学习多种方式上传图片—学习文档,大神绕行
一、分片上传图片
示例:提供两种方案:
1.先生成图片,每次追加写入读取的切片数据
2.先合并所有切片数据,直接生成完整图片
二、使用步骤
1.vue中HelloWorld.vue
代码如下(示例):
<template>
<div class="hello">
<h1>{{msg}}</h1>
<el-upload class="avatar-uploader" action="''" :http-request="uploadPath" :show-file-list="false" :before-upload="beforeAvatarUpload">
<img v-if="imgUrl" :src="imgUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</template>
<script>
import { addSourceImg, uploadPath } from "@/axios/index";
export default {
name: "HelloWorld",
data() {
return {
imgUrl: null,
msg: "", //"活在当下!",
};
},
methods: {
beforeAvatarUpload(file) {
const isImage = file.type === "image/jpeg" || "image/png";
const limitSize = file.size / 1024 / 1024 < 2;
if (!isImage) {
this.$message.error("上传头像图片只能是 JPG或png 格式!");
}
if (!limitSize) {
this.$message.error("上传头像图片大小不能超过 2MB!");
}
return isImage && limitSize;
},
async uploadPath(item) {
// const fileRender = new FileReader();
let file = item.file;
let size = file.size;
let CHUNK_SIZE = 1024 * 100; //512kb一次
let start = 0;
let end = 0;
let sourceId = new Date().getTime() + file.name.split(".")[0];
this.loadOnce(file, size, CHUNK_SIZE, start, end, sourceId);
},
loadOnce(file, size, CHUNK_SIZE, start, end, sourceId) {
//完成所有切片上传后生成图片
end = start + CHUNK_SIZE >= size ? size : start + CHUNK_SIZE;
let chunk = file.slice(start, end);
let fd = new FormData();
fd.append("data", chunk);
fd.append("sourceId", sourceId);
uploadPath(fd).then(res=>{
start = end;
console.log(size,end,"????????")
if(end<size){
this.loadOnce(file,size,CHUNK_SIZE,start,end,sourceId);
}else{
//传输完成
addSourceImg({sourceId});
}
});
},
},
};
</script>
2.vue中axios.js
代码如下(示例):
import axios from 'axios';
import { Loading, Message } from 'element-ui';
let urlData = { basicUrl: "http://127.0.0.1:3002" }
let loading;
const instance = axios.create({
baseURL: urlData.basicUrl,
timeout: 1000,
headers: { "X-Requested-With": "XMLHttpRequest" },
withCredentials: false,
});
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
loading = Loading.service({
lock: true, // 是否锁屏
text: '正在加载...', // 加载动画的文字
spinner: 'el-icon-loading', // 引入的loading图标
background: 'rgba(0, 0, 0, 0.3)', // 背景颜色
})
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
loading.close();
// 对响应数据做点什么
return response.data;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export async function addSourceImg(data) {
return instance.post('/addSourceImg', data);
}
export async function uploadPath(data) {
return instance.post('/uploadPath', data);
}
export default instance;
3.koa中routes.js
代码如下(示例):
import Router from 'koa-router';
import fs, { readFileSync } from 'fs';
import path, { resolve } from 'path';
let dirname = path.resolve();
let arr = dirname.split("\\");
dirname = arr.join('/');
const __dirname = dirname;
const router = new Router();
let uploadObj = {};
//保存上传文件切片地址
export const uploadPath = async (ctx, next) => {
const req = ctx.request.body;
let data = {
sourceId:req.sourceId,
sourceUrl:ctx.request.files.data.path
}
if(!uploadObj["sourceUrl"]){
uploadObj={};
uploadObj["sourceUrl"] = data.sourceUrl;
}else{
uploadObj["sourceUrl"] += ("###"+data.sourceUrl);
}
ctx.body = {
code: 200,
data: {},
msg: "",
}
}
//所有文件切片传输完成后,生成文件
export const addSourceImg = async ctx=> {
//存储上传文件切片的地址
let sourceUrlArr = uploadObj["sourceUrl"].split("###");
//先生成图片,每次追加写入读取的切片数据
// let ws = fs.createWriteStream(`./public/uploads/${ctx.request.body.sourceId}.png`);
// for(let i=0;i<sourceUrlArr.length;i++){
// let data = await fs.readFileSync(sourceUrlArr[i]);
// await ws.write(data);
// await fs.unlinkSync(sourceUrlArr[i]);
// }
// ws.end();
//先合并所有切片数据,直接生成完整图片
let readArr = [];
for(let i=0;i<sourceUrlArr.length;i++){
let data = await fs.readFileSync(sourceUrlArr[i]);
readArr.push(data);
await fs.unlinkSync(sourceUrlArr[i]);
}
let writeData = Buffer.concat(readArr);
await fs.writeFileSync(`./public/uploads/${ctx.request.body.sourceId}.png`,writeData);
ctx.body = {
code: 200,
data: {},
msg: "",
}
}
//SOURCE
router.post('/addSourceImg',addSourceImg);
router.post('/uploadPath',uploadPath);
export default router;
4.koa中app.js
代码如下(示例):
import koa from 'koa';
import cors from 'koa-cors';
import router from './routes/routes.js';
import staticFiles from 'koa-static';
import koaBody from 'koa-body';
import path from 'path';
const __dirname = path.resolve();
const app = new koa();
app.use(cors({ // 指定一个或多个可以跨域的域名
origin: function (ctx) { // 设置允许来自指定域名请求
if (ctx.url === '/') {
return "*"; // 允许来自所有域名请求, 这个不管用
}
// return 'http://localhost:8000'; // 这样就能只允许 http://localhost:8000 这个域名的请求了
return '*'; // 这样就能只允许 http://localhost:8000 这个域名的请求了
},
maxAge: 5, // 指定本次预检请求的有效期,单位为秒。
credentials: true, // 是否允许发送Cookie
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 设置所允许的HTTP请求方法
allowHeaders: ['Content-Type', 'Authorization', 'Accept'], // 设置服务器支持的所有头信息字段
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] // 设置获取其他自定义字段
}))
app.use(koaBody({
multipart: true,
formidable: {
//上传文件存储目录
uploadDir: path.join(__dirname, `/public/uploads/`),
//允许保留后缀名
keepExtensions: true,
multipart: true,
},
jsonLimit:'10mb',
formLimit:'10mb',
textLimit:'10mb'
})); //解析formdata过来的数据
app.use(router.routes());
app.use(router.allowedMethods());
app.use(staticFiles(__dirname + '/public'));
app.listen('3002');
console.log("项目启动,访问:","localhost:3002");
总结
踩坑路漫漫长@~@