工程实践
node+koa 实现图片的上传
[代码地址](https://github.com/dirkhe1051931999/common-demo) 服务端接口以及中间件 依赖的包 const Koa = require('koa'); // 解析成json const koaJson = require('koa-json'); // 解析body的 const bodyParser = requir
[代码地址](https://github.com/dirkhe1051931999/common-demo) 服务端接口以及中间件 依赖的包 const Koa = require('koa'); // 解析成json const koaJson = require('koa-json'); // 解析body的 const bodyParser = requir
[代码地址](https://github.com/dirkhe1051931999/common-demo)
const Koa = require('koa');
// 解析成json
const koaJson = require('koa-json');
// 解析body的
const bodyParser = require('koa-bodyparser');
// 静态资源库
const resource = require('koa-static');
// 设置session的
const session = require('koa-session');
const path = require('path');
const Router = require('koa-router');
const upload = require('./upload');
const app = new Koa();
let router = new Router();
// session使用前的一些设置
app.keys = ['some secret hurr'];
const CONFIG = {
key: 'koa:sess', //cookie key (default is koa:sess)
maxAge: 86400000, // cookie的过期时间 maxAge in ms (default is 1 days)
overwrite: true, //是否可以overwrite (默认default true)
httpOnly: true, //cookie是否只有服务器端可以访问 httpOnly or not (default true)
signed: true, //签名默认true
rolling: false, //在每次请求时强行设置cookie,这将重置cookie过期时间(默认:false)
renew: false //(boolean) renew session when session is nearly expired,
};
app.use(session(CONFIG, app));
router.post('/upload', async (ctx) => {
let result;
try {
result = await upload.uploadFile(ctx);
ctx.body = {
success: 0,
message: result.filePath
};
} catch (error) {
ctx.body = {
success: 1,
message: '缺少参数'
};
}
});
// upload.js
const fs = require('fs');
const path = require('path');
const moment = require('moment');
const formidable = require('formidable');
const config = {
root: path.normalize(__dirname),
appPath: 'static',
tempUploads: 'tempUploads',
uploads: 'uploads'
};
function mkdirsSync(dirname) {
if (fs.existsSync(dirname)) {
return true;
} else {
if (mkdirsSync(path.dirname(dirname))) {
fs.mkdirSync(dirname);
return true;
}
}
}
exports.uploadFile = async (ctx) => {
return new Promise((resolve, reject) => {
let form = new formidable.IncomingForm();
form.encoding = 'utf-8';
form.keepExtensions = true; // 保留后缀
form.maxFieldsSize = 2 * 1024 * 1024; // 文件大小2M
form.multiples = true;
form.uploadDir = path.join(config.root, config.appPath, config.tempUploads);
mkdirsSync(form.uploadDir);
form.parse(ctx.req, (err, fields, files) => {
if (err) {
reject(err);
}
let data = JSON.parse(fields.data);
if (files.uploadFile === undefined && data.poster !== '') {
return resolve({
fields: data,
filePath: data.poster
});
}
let filePath = '';
// 如果提交文件的form中将上传文件的input名设置为uploadFile,就从uploadFile中取上传文件。否则取for in循环第一个上传的文件。
if (files.uploadFile) {
filePath = files.uploadFile.path;
} else {
for (let key in files) {
if (files[key].path && filePath === '') {
filePath = files[key].path;
break;
}
}
}
// 文件移动的目录文件夹,不存在时创建目标文件夹
let dirName = moment().format('YYYYMMDD');
let targetDir = path.join(config.root, config.appPath, config.uploads, dirName);
mkdirsSync(targetDir);
// 以当前时间戳对上传文件进行重命名
let fileExt = filePath.substring(filePath.lastIndexOf('.'));
let fileName = new Date().getTime() + fileExt;
let targetFile = path.join(targetDir, fileName);
// 移动文件
fs.rename(filePath, targetFile, (err) => {
if (err) {
reject(err);
} else {
//上传成功,返回文件的相对路径
return resolve({
fields: data,
filePath: path.join(path.sep, config.uploads, dirName, fileName)
});
}
});
});
ctx.session.fileUploadProgress = 0;
form.on('progress', (bytesReceived, bytesExpected) => {
// 百分比
let percent = Math.round((bytesReceived / bytesExpected) * 100);
console.log('precent', percent + '%');
ctx.session.fileUploadProgress = percent;
});
form.on('end', () => {
ctx.session.fileUploadProgress = 100 + '%';
});
});
};
// 装载
app.use(bodyParser());
app.use(koaJson());
app.use(resource(path.join(path.normalize(__dirname), 'static')));
app.use(router.routes()).use(router.allowedMethods());
app.listen(1112, () => {
console.log('listen,1112');
});
特别注意在使用 ctx.session 的时候,一定先要装载 session 的中间件
<div>
<img :src="imgSrc" alt="" />
<p>{{msg}}</p>
<div class="custom" @click="addFile">上传[自定义]</div>
<div class="custom" @click="deleteFile">取消上传[自定义]</div>
<p>type="file"的按钮可以隐藏掉</p>
<input type="file" ref="file" accept="image/png,image/jpeg" @change="fileChanged" />
<button @click="confirm">确认上传</button>
</div>
这里面有一个小技巧,点击自定义的按钮,触发 type='file'的事件:
this.$refs.file.click()
methods:{
fileChanged() {
const newFile = this.$refs.file.files[0];
if (
newFile.type.indexOf('image/png') === -1 &&
newFile.type.indexOf('image/jpeg') === -1
) {
this.$refs.file.value = '';
alert("只接受png和jpeg的图片")
return;
}
if (newFile.size > this.maxSize * 1024) {
alert(`上传图片最大不能超过${this.maxSize}kb`)
this.$refs.file.value = '';
return;
}
if (this.file.name !== newFile.name || this.file.size !== newFile.size) {
// 将图片文件转成BASE64格式
this.html5Reader(newFile);
}
this.$refs.file.value = '';
},
html5Reader(file) {
const reader = new FileReader();
reader.onload = e => {
this.imgSrc = e.target.result;
let image = new Image();
image.onload = () => {
let width = image.width;
let height = image.height;
if (width / height >= 1.8 && width / height <= 2.2) {
this.file = file;
} else {
alert(`上传图片的宽/高比要求在1.8-2.2之间`)
this.imgSrc = '';
this.$refs.file.value = '';
}
};
image.src = e.target.result;
};
reader.readAsDataURL(file);
},
confirm() {
let laboratory = {};
laboratory['poster'] = this.imgSrc;
// 创建from表单
let formData = new FormData();
formData.append('uploadFile', this.file);
formData.append('data', JSON.stringify(laboratory));
// 定义接口:需要代理
const url = "/api/upload"
// 定义header头
let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
// 把请求时间加长
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.timeout = 20000;
// 请求
axios.post(url, formData, config).then((res) => {
this.msg = `http://127.0.0.1:1112${res.data.message}`
}, (err) => {
})
}
}
{
proxyTable: {
'/api': {
target: "http://127.0.0.1:1112",
changeOrigin: true,
pathRewrite: {
'^/api': ""
}
}
}
}
在使用前端请求接口前,做两件事:1、设置 Content-Type = 'multipart/form-data' 2、代理接口
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论