因为现在的项目要求上传图片至阿里云而且支持图片的校验。在网上扒了很多博客。。。很徒劳。基本都是互相抄,功能缺失严重,所以只能自己实现一个,放在博客上面,干货可以拿走!!有需求可以直接拿,别忘了点个赞!!!
有问题请求留言。有关其他的一些引入的文件,我后续会补上,都是一些依赖!
首先看一下样式:未上传前:

上传后的样式:

其实样式还是蛮可以的对吧!!!
除render中的部分传参逻辑需要自己定义外,其他完全可以直接使用。
话不多说直接上代码,首先父组件中引入该组件
//引入组件,路径按照自己定义的路径。
import Confirm from './confirm/index.jsx';
//引用该组件 参数自己按照自己的需求传递,用于上传图片之后重命名!
<UploadDom
userId={this.state.userId}
userName={this.state.userName}
groupId={this.state.groupId}
extend={this.extend}
/>
然后就是最关键的upload组件的实现!
import React, { Component, PureComponent } from 'react';
import PropTypes from 'prop-types';
import bindAll from 'lodash.bindall';
import { Base64 } from 'js-base64';
import plupload from 'plupload';
import './Upload.css';
import './lib/crypto1/crypto/crypto.js';
import './lib/crypto1/hmac/hmac.js';
import './lib/crypto1/sha1/sha1.js';
let config = {
name: '老男的demo',
prefix: '',
upload: 'yunweihuigaosunizenmepeizhi1',
download: 'yunweihuigaosunizenmepeizhi2',
accessid: 'yunweihuigaosunizenmepeizhi3',
videoAccessid: 'yunweihuigaosunizenmepeizhi4',
accesskey: 'yunweihuigaosunizenmepeizhi5',
videoAccesskey: 'yunweihuigaosunizenmepeizhi6',
policyText: {
"expiration": "2020-01-01T12:00:00.000Z", //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了
"conditions": [
["content-length-range", 0, 1048576000] // 设置上传文件的大小限制
]
},
dirname: "laonan/",
finished: true,
userId: 1,
userName: "texting",
groupId: 1,
uploadedList: [],
files: {
limit: 0,
size: 120 * 1024, // Byte
sizeMin: 40 * 1024,
progress: false, // 进度条
extensions: ["jpg", "png"],
repetition: true, // 不允许重复(true 不允许)
img: {
size: [626, 413],
limit: [">", ">"]
}
},
needLogin: false,
ossUrl: 'yunweihuigaosunizenmepeizhi7'
}
let fileName = '点击选择参赛者2寸证件照';
const initFile = {
fileName: '点击选择参赛者2寸证件照',
fileState: '',
filePercent: 0,
fileErr: '',
};
class UploadDom extends Component {
constructor(props) {
super(props);
bindAll(this, [
'computeSize',
'removeFile',
'listKillFile',
'getSuffix',
'addFileFilterImg',
'contrast'
]);
this.state = {
fileList: [],
uploading: false,
fileName: '您的浏览器不支持flash,Silverlight或者HTML5!',
fileState: '',
filePercent: 0,
fileErr: '123',
isOk: false,
photoUrl: '',
btnContent: '点击上传',
btnStyle: 'upload-btn',
uploader: {}
};
}
componentWillMount(){
}
componentDidMount() {
config.files.img && this.addFileFilterImg();
this.state.uploader = new plupload.Uploader({
runtimes: 'html5,flash,silverlight,html4',
browse_button: `selectImage_recipt`,
url: config.ossUrl,
multi_selection: false,
filters: {
mime_types: config.files.extensions.length ? [{ extensions: config.files.extensions.join() }] : undefined,
prevent_duplicates: config.files.repetition,
max_file_size: config.files.size,
min_file_size: config.files.sizeMin,
imgSize: config.files.img.size
},
flashSwfUrl: "plupload/js/Moxie.swf",
init: {
PostInit: file => {
this.setState({
fileName: '点击选择参赛者2寸证件照',
});
document.getElementById(`uploadImage_recipt`).onclick = () => {
this.setUploadParam(this.state.uploader, '', false);
return false;
};
},
BeforeUpload: (up, file) => {
this.setUploadParam(up, file.name, true);
},
FileFiltered:(up, file) => {
if (false) {
if (
config.files.limit &&
up.files.length > config.files.limit
) {
up.removeFile(file);
} else {
config.finished = false;
}
} else {
if (up.files.length > 1) {
this.listKillFile(up.files[0]);
this.removeFile(up.files[0]);
}
config.finished = false;
}
},
FilesAdded: (up, files) => {
plupload.each(files, file => {
this.setState({
...initFile,
fileName: `${file.name}(${plupload.formatSize(file.size)})`,
});
});
},
UploadProgress: (up, file) => {
this.setState({
fileState: `${file.percent}%`,
filePercent: file.percent,
});
},
FileUploaded: (up, file, info) => {
if (info.status === 200) {
this.setState({
fileState: '成 功',
btnContent: '已上传',
btnStyle:'upload-btn-end'
});
// 这一块没什么用 可以删掉
let { isUp } = this.props
if (isUp === true) {
this.props.uploaded(`${config.download}${config.dirname}${fileName}`)
} else {
let filesData = {
[this.props.tag]: `${
this.props.uploadType && this.props.uploadType === 'video'
? config.videoDownload
: config.download
}${config.dirname}${fileName}`,
};
}
} else {
this.setState({
fileState: info.response,
});
}
},
Error: (up, err) => {
this.setState({
fileErr: `Error xml:${err.response}`,
});
switch (err.code) {
case -200:
this.setState({
fileErr: `网络发生错误, error, 3000`,
});
console.log("网络发生错误", "error", 3000);
break;
case -300:
this.setState({
fileErr: `磁盘读写错误, error, 3000`,
});
console.log("磁盘读写错误", "error", 3000);
break;
case -600:
this.setState({
fileErr: `上传文件体积不能超过${this.computeSize(config.files.size)},error,3000`,
});
console.log(
`上传文件体积不能超过${this.computeSize(config.files.size)}`,
"error",
3000
);
break;
case -601:
this.setState({
fileErr: `选择的文件类型不符合要求, error, 3000`,
});
console.log("选择的文件类型不符合要求", "error", 3000);
break;
case -602:
this.setState({
fileErr: `选取文件重复, error, 3000`,
});
console.log("选取文件重复", "error", 3000);
break;
default:
this.setState({
fileErr: `Error xml:${err}`,
});
console.log(err);
}
},
},
});
this.state.uploader.init();
}
computeSize(size) {
let sizeStr = ``;
if (size < 1024) {
sizeStr = `${size}B`;
} else if (size >= 1024 && size < 1024 * 1024) {
sizeStr = `${(size / 1024).toFixed(1)}KB`;
} else if (size >= 1024 * 1024 && size < 1024 * 1024 * 1024) {
sizeStr = `${(size / 1024 / 1024).toFixed(1)}MB`;
} else if (
size >= 1024 * 1024 * 1024 &&
size < 1024 * 1024 * 1024 * 1024
) {
sizeStr = `${(size / 1024 / 1024 / 1024).toFixed(1)}GB`;
}
return sizeStr;
}
removeFile(file) {
if (file.status === 2) {
this.state.uploader.stop();
console.log("上传已终止", "warning", 3000);
}
this.state.uploader.removeFile(file);
config.finished = true;
this.state.uploader.files.some((item) => {
if ([1, 2].includes(item.status)) {
config.finished = false;
return true;
}
});
this.listKillFile(file);
}
listKillFile(file) {
config.uploadedList.some((item, i, arr) => {
if (item.id === file.id) {
arr.splice(i, 1);
return true;
}
});
this.state.fileList.some((item, i, arr) => {
if (item.id === file.id) {
arr.splice(i, 1);
return true;
}
});
}
getSuffix(filename) {
let pos = filename.lastIndexOf('.');
let suffix = '';
if (pos !== -1) {
suffix = filename.substring(pos);
}
return suffix;
}
// 添加自定义 过滤类型
addFileFilterImg() {
plupload.addFileFilter("imgSize", (imgSize, file, cb) => {
if (file.type.includes("image") && !file.type.includes("gif")) {
let img = new plupload.moxie.image.Image();
img.onload = () => {
let flag = true;
const msg = [];
const imgMsg = [
this.contrast(img.width, 0),
this.contrast(img.height, 1)
];
imgMsg.forEach((item) => {
if (!item.flag) {
flag = false;
}
if (item.rule) {
msg.push(`图片${item.type}需${item.rule}${item.imgSize}像素`);
}
});
if (msg.length > 0) {
console.log(`${msg.join(",")}`, "error", 3000);
}
img.destroy();
img = null;
cb(flag);
};
img.onerror = () => {
console.log("imgOnError", img);
img.destroy();
img = null;
cb(false);
};
img.load(file.getSource());
} else {
cb(true);
}
});
}
contrast(size, num) {
let flag = false;
let rule = "";
const imgSize = config.files.img.size[num];
let type = "宽";
if (num === 1) {
type = "高";
}
switch (config.files.img.limit[num]) {
case "=":
if (size === imgSize) {
flag = true;
} else {
rule = "等于";
}
break;
case "!=":
if (size !== imgSize) {
flag = true;
} else {
rule = "不等于";
}
break;
case ">":
if (size > imgSize) {
flag = true;
} else {
rule = "大于";
}
break;
case "<":
if (size < imgSize) {
flag = true;
} else {
rule = "小于";
}
break;
case ">=":
if (size >= imgSize) {
flag = true;
} else {
rule = "大于等于";
}
break;
case "<=":
if (size <= imgSize) {
flag = true;
} else {
rule = "小于等于";
}
break;
default:
if (size === imgSize) {
flag = true;
} else {
rule = "等于";
}
}
return {
flag,
rule,
type,
imgSize
};
}
setUploadParam(up, filename, ret) {
fileName = this.props.userId+ '_'+ this.props.groupId+ '_'+ this.props.userName + this.getSuffix(filename);
const policyBase64 = Base64.encode(JSON.stringify(config.policyText));
const bytes = Crypto.HMAC(
Crypto.SHA1,
policyBase64,
config.accesskey,
{
asBytes: true,
}
);
const wholeUrl =config.upload +'/'+ config.dirname + fileName;
console.log(wholeUrl);
this.setState({
photoUrl: wholeUrl
})
this.props.extend(wholeUrl);
const signature = Crypto.util.bytesToBase64(bytes);
const new_multipart_params = {
key: config.dirname + fileName,
policy: policyBase64,
OSSAccessKeyId: config.accessid,
success_action_status: '200', //让服务端返回200,不然,默认会返回204
signature: signature,
Filename: 'console/',
multi_selection: false,
};
up.setOption({
url: config.upload,
multipart_params: new_multipart_params,
});
if (typeof this.props.before === 'string') {
up.start();
}
}
render() {
return (
<div className="container">
<div className="upload-box">
<span id={`selectImage_recipt`} className="uploadImage">
{this.state.fileName}
</span>
<span id={`uploadImage_recipt`} className={this.state.btnStyle}>
{this.state.btnContent}
</span>
</div>
<p
className="schedule"
style={{
width: this.state.filePercent + '%',
height: this.state.filePercent > 0 ? 6 : 0,
}}
/>
<div className="uoload-tips">用以制作考试证件,像素为626*413,格式为jpg或png,大小40KB-120KB</div>
<pre className="pre-upload">{this.state.fileErr}</pre>
</div>
);
}
}
UploadDom.defaultProps = {
before: '',
isShowClear: false
}
export default UploadDom;