express,koa2等node处理前端上传图片并保存到文件中

7,644 阅读4分钟

在实际的项目中,图片的处理往往是最麻烦的,无论是前后台,我自己也试手了一两个图片上传的小项目,把步骤写下来,以后自己忘记可以返回来看一下,同时希望能够帮到小伙伴们...

前端网页

框架: vue + iview等组件来实现图片的file功能 + koa2

运用iview的Upload组件

Upload 上传

upload组件方法方式就不过多一一赘述了,在这里提几点关键点

<Upload
:on-format-error="handleFormatError"
    :before-upload="handleBeforeUpload"
    :data='uploadData' // 传参
    multiple
    action="//localhost:6001/blog/admin/edit/image" // http请求路径
></Upload>

data() {
    return {
        uploadData: {
        	url: '', // 图片二进制的data的url
        	articleId: '' // 这个详情的ID 后台与他绑在一起
        }
    }
}
// 利用浏览器的FileReader的特性压缩并上传图片
handleBeforeUpload() {
    let reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = (e)=> {
        file.url = reader.result;
        this.uploadData.articleId = this.$route.query.id;
        this.uploadData.url = file.url;
    }
}

前端总结: 运用iview组件来构造file上传图片本身不难,官方了解api多次尝试就可以了

koa后台node框架

node后台处理图片,查了网上资料,采用了formidable的包来处理图片的,express采用formidable的npm包,而koa2采用了koa-formidable的npm包,两者没多大区别,koa-formidable只不过是formidable的再度封装和运载koa2的框架上而已

我们先看浏览器查看http的请求

图片的http请求为什么跟别的不一样,就是不一样,我们不一样,哈哈!别钻牛角,适应就行了

  • 1.引入koa-formidable
  • 2.下载地址,API
  • let form = formidable.parse(request);
  • 图片重命名fs,放入public文件中
  • 图片路径放回给前端

步骤并不复杂,只是本人对于node的模块不太熟悉,比如fs path模块,希望以后多多做node小项目,熟悉起来...

代码如下:

// app.js
const Router = require('koa-router');
const router = new Router();
const serve = require("koa-static");

const formidable = require('koa-formidable'); // 图片处理
const fs = require('fs'); // 图片路径
const path = require('path'); // 图片路径

app.use(serve(__dirname))  // 设置静态文件

// 新建文件,可以去百度fs模块
let mkdirs = (dirname, callback)=> {
    fs.exists(dirname, function(exists) {
        if (exists) {
            callback();
        } else {
            mkdirs(path.dirname(dirname), function() {
                fs.mkdir(dirname, callback);
            });
        }
    });
};

router.post('/upload/image', function (ctx, next) {
    let form = formidable.parse(request);
    function formImage() {
    	return new Promise((resolve, reject) => {
    		form((opt, {fields, files})=> {
        	let url = fields.url;
        	let articleId = fields.articleId;
        	let filename = files.file.name;
        	console.log(files.file.path);
        	let uploadDir = 'public/upload/';
    	        let avatarName = Date.now() + '_' + filename;
    	        mkdirs('public/upload', function() {
                	fs.renameSync(files.file.path, uploadDir + avatarName); //重命名
                	resolve(config[env].host + '/' + uploadDir + avatarName)
                	// http://localhost:6001/public/upload/1513523744257_WX20171205-150757.png
                })
            })
    	})
    }
    let url = await formImage();
    return {flag: '1',msg:'',data: url} // 路径返回给前端
});

app
  .use(router.routes())
  .use(router.allowedMethods());

要点


1.设置静态文件 app.use(serve(__dirname))

根据个人喜好设置静态路径
默认的设置方法: 
app.use(serve(__dirname) + '/public') 
resolve(config[env].host + '/upload/' + avatarName)

也可以这样设置,
app.use(serve(__dirname) + '/public/upload') 
resolve(config[env].host + '/' + avatarName)

2.fs.exists新建文件,用于存放图片

新建一个匿名函数来回调判断有无该文件,有则存入,无则新建文件

let mkdirs = (dirname, callback)=> {
    fs.exists(dirname, function(exists) {
        if (exists) {
            callback();
        } else {
            mkdirs(path.dirname(dirname), function() {
                fs.mkdir(dirname, callback);
            });
        }
    });
};

3.koa框架存在异步,所以必须用await来终结异步操作

至于async/await可以去百度一下

function formImage(){
    return new Promise((resolve)=> {
        // 处理图片
        resolve(url)
    })
}
let url = await formImage();

4.formidable处理图片,获取图片

formidable API

let form = formidable.parse(request);
form((opt, obj)=> {
    // 代码如上
})
// 因为obj是个对象并有fields, files两个参数,那我直接这样处理:
form((opt, {fields, files})=> {
    // 代码如上
    // 上面的代码最重要的就是下面这一句 
    // fs.renameSync(opt1,opt2) opt1 form处理的图片路径而不是fileds.url二进制的路径, opt2则是重命名的图片名称
    fs.renameSync(files.file.path, uploadDir + avatarName);
})

做完就可以 ctx.body = {data: url, msg: '', flag: '1'} 返回给前端

在另个项目中,同样的代码却报错了... 我也是一脸懵逼

以上是在mac笔记本操作,没有发现错误,但在window系统下却发现图片上传不成功,一个致命的错误

fs.js:439
  return binding.rename(pathModule._makeLong(oldPath),
                 ^
Error: EXDEV, cross-device link not permitted 'C:\Users\CLi\AppData\Local\Temp\df99513a93a1cbfbc26e076f8ae08b92'
    at Object.fs.renameSync (fs.js:439:18)

查询了报错的原因:是跨分区重命名文件,会有权限问题

网友解决方法之一:Node.js中所用的fs.renameSync出错:Error: EXDEV, cross-device link not permitted

用了以上文章的解决方法: 也报错了 util.pump is not undefined,这下懵逼了
用了node的pipe流方法

let readStream = fs.createReadStream(files.file.path)
let writeStream = fs.createWriteStream(uploadDir + avatarName);
readStream.pipe(writeStream);

代码如下:

exports.editImages = async(ctx, next)=> {
	let form = formidable.parse(ctx.request);
    form.encoding = 'utf-8';
    form.keepExtensions = true; //保留后缀
    mkdirs('public/upload');
    let imgPlay = new Promise((resolve, reject) => {
	form((opt, {fields, files})=> {
	let articleId = fields.articleId;
	let filename = files.file.name;
        let avatarName = Date.now() + '_' + filename;
        let readStream = fs.createReadStream(files.file.path)
		let writeStream = fs.createWriteStream(uploadDir + avatarName);
		readStream.pipe(writeStream);
                // fs.rename(files.file.path, uploadDir + avatarName); //window报错了重命名
        	resolve({
        		url: config[env].host + '/' + uploadDir + avatarName
        	})
        	// http://localhost:6001/public/upload/1513523744257_WX20171205-150757.png
    })
    });
    let imageData = await imgPlay;
	ctx.body = {flag: '1' ,msg:'',data: imageData}
}

完美解决跨平台的图书上传问题

项目中还有不如,如:前端上传图片的压缩问题,涉及多张上传问题,都没有实践,以后继续更新不足



这是我试水node的小项目的案例,请多指教

项目github地址:github.com/Jaction/blo…