热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于Node的React图片上传组件完成

写在前面红旗不倒,誓把JavaScript进行到底!本日引见我的开源项目Royal里的图片上传组件的前后端完成道理(React+Node),花了一些时刻,愿望对你有所协助。前端完成
写在前面

红旗不倒,誓把Javascript进行到底!本日引见我的开源项目 Royal 里的图片上传组件的前后端完成道理(React + Node),花了一些时刻,愿望对你有所协助。

《基于Node的React图片上传组件完成》

前端完成

遵照React 组件化的头脑,我把图片上传做成了一个自力的组件(没有其他依靠),直接import即可。

import React, { Component } from 'react'
import Upload from '../../components/FormControls/Upload/'
//......
render() {
return (


)
}

uri 参数是必需传的,是图片上传的后端接口地点,接口怎样写下面会讲到。

衬着页面

组件render部份须要表现三个功用:

  • 图片拔取(dialog窗口)

  • 可拖拽功用(拖拽容器)

  • 可预览(预览列表)

  • 上传按钮 (button)

  • 上传完成图片地点和链接 (信息列表)

render函数

render() {
return (




OnChange={(v)=>this.handleChange(v)}
type="file"
size={this.state.size}
name="fileSelect"
accept="image/*"
multiple={this.state.multiple} />
OnDragOver={(e)=>this.handleDragHover(e)}
OnDragLeave={(e)=>this.handleDragHover(e)}
OnDrop={(e)=>this.handleDrop(e)}
className="upload-drag-area">
也许将图片拖到此处


"upload-preview":"upload-preview ry-hidden"}>
{ this._renderPreview(); // 衬着图片预览列表 }


"upload-submit":"upload-submit ry-hidden"}>



{ this._renderUploadInfos(); // 衬着图片上传信息 }



)
}

衬着图片预览列表

_renderPreview() {
if (this.state.files) {
return this.state.files.map((item, idx) => {
return (



{item.name}
className="upload-delete"
title="删除" index={idx}>





"upload-progress":
"upload-progress ry-hidden"}>
{this.state.progress[idx]}


)
})
} else {
return null
}
}

衬着图片上传信息列表

_renderUploadInfos() {
if (this.state.uploadHistory) {
return this.state.uploadHistory.map((item, idx) => {
return (


上传胜利,图片地点是:

检察


);
})
} else {
return null;
}
}

文件上传

前端要完成图片上传的道理就是经由过程构建FormData对象,把文件对象append()到该对象,然后挂载在XMLHttpRequest对象上 send() 到效劳端。

猎取文件对象

猎取文件对象须要借助 input 输入框的 change 事宜来猎取 句柄参数 e

OnChange={(e)=>this.handleChange(e)}

然后做以下处置惩罚:

e.preventDefault()
let target = event.target
let files = target.files
let count = this.state.multiple ? files.length : 1
for (let i = 0; i files[i].thumb = URL.createObjectURL(files[i])
}
// 转换为真正的数组
files = Array.prototype.slice.call(files, 0)
// 过滤非图片范例的文件
files = files.filter(function (file) {
return /image/i.test(file.type)
})

这时刻 files 就是 我们须要的文件对象构成的数组,把它 concat 到原有的 files里。

this.setState({files: this.state.files.concat(files)})

云云,接下来的操纵 就能够 经由过程 this.state.files 取到当前已选中的 图片文件。

应用Promise处置惩罚异步上传

文件上传关于浏览器来讲是异步的,为了处置惩罚 接下来的多图上传,这里引入了 Promise来处置惩罚异步操纵:

upload(file, idx) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
if (xhr.upload) {
// 上传中
xhr.upload.addEventListener("progress", (e) => {
// 处置惩罚上传进度
this.handleProgress(file, e.loaded, e.total, idx);
}, false)
// 文件上传胜利或是失利
xhr.Onreadystatechange= (e) => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 上传胜利操纵
this.handleSuccess(file, xhr.responseText)
// 把该文件从上传行列中删除
this.handleDeleteFile(file)
resolve(xhr.responseText);
} else {
// 上传失足处置惩罚
this.handleFailure(file, xhr.responseText)
reject(xhr.responseText);
}
}
}
// 最先上传
xhr.open("POST", this.state.uri, true)
let form = new FormData()
form.append("filedata", file)
xhr.send(form)
})
}

上传进度盘算

应用XMLHttpRequest对象发异步要求的优点是能够 盘算要求处置惩罚的进度,这是fetch所不具备的。
我们能够为 xhr.upload 对象的 progress 事宜增加事宜监听:

xhr.upload.addEventListener("progress", (e) => {
// 处置惩罚上传进度
this.handleProgress(file, e.loaded, e.total, i);
}, false)

申明:idx参数是记载多图上传行列的索引

handleProgress(file, loaded, total, idx) {
let percent = (loaded / total * 100).toFixed(2) + '%';
let _progress = this.state.progress;
_progress[idx] = percent;
this.setState({ progress: _progress }) // 反应到DOM里显现
}

拖拽上传

拖拽文件关于HTML5来讲实在异常简朴,由于它自带的几个事宜监听机制能够直接做这类处置惩罚。重要用到的是下面三个:

OnDragOver={(e)=>this.handleDragHover(e)}
OnDragLeave={(e)=>this.handleDragHover(e)}
OnDrop={(e)=>this.handleDrop(e)}

作废拖拽时的浏览器行动:

handleDragHover(e) {
e.stopPropagation()
e.preventDefault()
}

处置惩罚拖拽进来的文件:

handleDrop(e) {
this.setState({progress:[]})
this.handleDragHover(e)
// 猎取文件列表对象
let files = e.target.files || e.dataTransfer.files
let count = this.state.multiple ? files.length : 1
for (let i = 0; i files[i].thumb = URL.createObjectURL(files[i])
}
// 转换为真正的数组
files = Array.prototype.slice.call(files, 0)
// 过滤非图片范例的文件
files = files.filter(function (file) {
return /image/i.test(file.type)
})
this.setState({files: this.state.files.concat(files)})
}

多图同时上传

支撑多图上传我们须要在组件挪用途设置属性:

multiple = { true } // 开启多图上传
size = { 50 } // 一次最大上传数目(虽没有上限,为保证效劳端一般,发起50以下)

然后我们能够运用 Promise.all() 处置惩罚异步操纵行列:

handleUpload() {
let _promises = this.state.files.map((file, idx) => this.upload(file, idx))
Promise.all(_promises).then( (res) => {
// 悉数上传完成
this.handleComplete()
}).catch( (err) => { console.log(err) })
}

好了,前端事情已完成,接下来就是Node的事情了。

后端完成

为了轻易,后端采纳的是express框架来疾速搭建Http效劳和路由。详细项目见我的github node-image-upload。逻辑虽然简朴,但照样有几个可圈可点的处所:

跨域挪用

本项目后端采纳的是express,我们能够经由过程 res.header() 设置 要求的 “许可源” 来许可跨域挪用:

res.header('Access-Control-Allow-Origin', '*');

设置为 * 申明许可任何 接见源,不太平安。发起设置成 你须要的 二级域名,如 jafeney.com

除了 “许可源” ,其他另有 “许可头” 、”许可域”、 “许可要领”、”文本范例” 等。经常使用的设置以下:

function allowCross(res) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
}

ES6下的Ajax要求

ES6作风下的Ajax要乞降ES5不太一样,在正式的要求发出之前都邑先发一个 范例为 OPTIONS的要求 作为探索,只有当该要求经由过程今后,正式的要求才发向效劳端。

所以效劳端路由 我们还须要 处置惩罚如许一个 要求:

router.options('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});

注重:该要求一样须要设置跨域。

处置惩罚上传

处置惩罚上传的图片惹人了multiparty模块,用法很简朴:

/*运用multiparty处置惩罚上传的图片*/
router.post('/upload', function(req, res, next) {
// 天生multiparty对象,并设置上传目的途径
var form = new multiparty.Form({uploadDir: './public/file/'});
// 上传完成后处置惩罚
form.parse(req, function(err, fields, files) {
var filesTmp = JSON.stringify(files, null, 2);
var relPath = '';
if (err) {
// 保留失利
console.log('Parse error: ' + err);
} else {
// 图片保留胜利!
console.log('Parse Files: ' + filesTmp);
// 图片处置惩罚
processImg(files);
}
});
});

图片处置惩罚

Node处置惩罚图片须要引入 gm 模块,它须要用 npm 来装置:

npm install gm --save

BUG申明

注重:node的图形操纵gm模块前运用必需 先装置 imagemagickgraphicsmagickLinux (ubuntu)上运用apt-get 装置:

sudo apt-get install imagemagick
sudo apt-get install graphicsmagick --with-webp // 支撑webp花样的图片

MacOS上能够用 Homebrew 直接装置:

brew install imagemagick
brew install graphicsmagick --with-webp // 支撑webp花样的图片

预设尺寸

有些时刻除了原图,我们能够须要把原图等比例减少作为预览图也许缩略图。这个异步操纵照样用Promise来完成:

function reSizeImage(paths, dstPath, size) {
return new Promise(function(resolve, reject) {
gm(dstPath)
.noProfile()
.resizeExact(size)
.write('.' + paths[1] + '@' + size + '00.' + paths[2], function (err) {
if (!err) {
console.log('resize as ' + size + ' ok!')
resolve()
}
else {
reject(err)
};
});
});
}

重定名图片

为了轻易排序和治理图片,我们根据 “年月日 + 时刻戳 + 尺寸” 来定名图片:

var _dateSymbol = new Date().toLocaleDateString().split('-').join('');
var _timeSymbol = new Date().getTime().toString();

至于图片尺寸 运用 gmsize() 要领来猎取,处置惩罚方式以下:

gm(uploadedPath).size(function(err, size) {
var dstPath = './public/file/' + _dateSymbol + _timeSymbol
+ '_' + size.width + 'x' + size.height + '.'
+ _img.originalFilename.split('.')[1];
var _port = process.env.PORT || '9999';
relPath = 'http://' + req.hostname + ( _port!==80 ? ':' + _port : '' )
+ '/file/' + _dateSymbol + _timeSymbol + '_' + size.width + 'x'
+ size.height + '.' + _img.originalFilename.split('.')[1];
// 重定名
fs.rename(uploadedPath, dstPath, function(err) {
if (err) {
reject(err)
} else {
console.log('rename ok!');
}
});
});
总结

关于大前端的事情,明白图片上传的前后端道理仅仅是浅层的。我们的标语是 “把Javascript进行到底!”,如今无论是 ReactNative的挪动端开辟,照样NodeJS的后端开辟,前端工程师能够做的事情早已不仅仅是局限于web页面,它已渗入到了互联网应用层面的各个方面,也许,叫 全栈工程师 更加贴切吧。

固然,全栈 两个字的重量很重,不积跬步,无以致千里,功力低下的我还须要不停修炼和实践!

参考

张鑫旭 《基于HTML5的可预览多图片Ajax上传》

@迎接关注我的 github 和 个人博客 -Jafeney


推荐阅读
author-avatar
喜怒哀乐168_572
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有