react 实现oss阿里云上传图片功能【支持图片格式、大小、像素校验、重命名】

1,586 阅读4分钟

因为现在的项目要求上传图片至阿里云而且支持图片的校验。在网上扒了很多博客。。。很徒劳。基本都是互相抄,功能缺失严重,所以只能自己实现一个,放在博客上面,干货可以拿走!!有需求可以直接拿,别忘了点个赞!!!

有问题请求留言。有关其他的一些引入的文件,我后续会补上,都是一些依赖!

首先看一下样式:未上传前:

上传后的样式:

其实样式还是蛮可以的对吧!!!

除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;

css 就补贴不提出来了!后续可以用上面的链接下载完整代码