Vuecli图片裁剪之后上传到阿里云OSS

如题,做一个图片裁剪功能,前端使用vue-cropper (https://github.com/xyxiao001/vue-cropper)将图片裁剪之后请求Nodejs写的接口,并且上传到阿里云OSS

贴出关键的代码,UI组件是自己写的,所以不用管,装依赖,main.js中先引入组件,装B一气呵成,当然你也可以在页面中单独引入

npm install vue-cropper 
import VueCropper from 'vue-cropper'
Vue.use(VueCropper)

前端关键代码

<template>
    <div class="kom-transition-box">
        <div class="upload-photo">上传照片<input type="file" @change="changeUpload($event)"/></div>
    
        <!-- 一个弹出层 -->
        <kom-popup v-model="showImagePopover" position="center" width="90%">
            <div>
                <div class="cropper-content">
                    <div class="cropper">
                        <vueCropper
                            ref="cropper"
                            :img="option.img"
                            :outputSize="option.size"
                            :outputType="option.outputType"
                            :info="true"
                            :full="option.full"
                            :canMove="option.canMove"
                            :canMoveBox="option.canMoveBox"
                            :original="option.original"
                            :autoCrop="option.autoCrop"
                            :fixed="option.fixed"
                            :fixedNumber="option.fixedNumber"
                            :centerBox="option.centerBox"
                            :infoTrue="option.infoTrue"
                            :fixedBox="option.fixedBox"
                        ></vueCropper>
                    </div>
                </div>
                <div>
                    <kom-button size="large" type="primary" @click.native="finish" :loading="loading">确认</kom-button>
                    <kom-button size="large" type="hollow" @click.native="showImagePopover = false">取 消</kom-button>
                </div>
            </div>
        </kom-popup>
    </div>
</template>

<script>
    import {mapState} from 'vuex'

    export default {
        data() {
            return {
                imgData: {
                    accept: 'image/gif, image/jpeg, image/png, image/jpg'
                },
                showImagePopover: false,
                option: {  // 裁剪组件的基础配置option
                    img: '', // 裁剪图片的地址
                    info: true, // 裁剪框的大小信息
                    outputSize: 0.4, // 裁剪生成图片的质量
                    outputType: 'jpeg', // 裁剪生成图片的格式
                    canScale: true, // 图片是否允许滚轮缩放
                    autoCrop: true, // 是否默认生成截图框
                    autoCropWidth: 100, // 默认生成截图框宽度
                    autoCropHeight: 100, // 默认生成截图框高度
                    fixedBox: true, // 固定截图框大小 不允许改变
                    fixed: true, // 是否开启截图框宽高固定比例
                    fixedNumber: [1, 1], // 截图框的宽高比例
                    full: false, // 是否输出原图比例的截图
                    canMoveBox: false, // 截图框能否拖动
                    original: true, // 上传图片按照原始比例渲染
                    centerBox: true, // 截图框是否被限制在图片里面
                    infoTrue: false // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
                },
                loading: false, // 防止重复提交
                imgTargetInfo: {} // 选择的图片信息
            }
        },
        computed: {
            ...mapState({ // 从vuex中获取用户信息
                userInfo: state => state.userInfo
            })
        },
        methods: {
            changeUpload(event) {
                let imgTarget = event.target.files[0];
    
                if(!imgTarget){ // 防止不选择文件直接关闭弹窗获取不到event报错的问题
                    return;
                }
                
                let type = imgTarget.type; // 文件的类型,判断是否是图片
                let size = imgTarget.size; // 文件的大小,判断图片的大小
                
                if (this.imgData.accept.indexOf(type) == -1) {
                    this.$toast("格式不支持", {
                        location: 'bottom'
                    });
                    return false;
                }
                if (size > 1024 * 1024 * 5) {
                    this.$toast("请选择5M以内的图片", {
                        location: 'bottom'
                    });
                    return false;
                }
                
                
                /* 关键代码 */
                var reader = new FileReader();
                reader.onload = (e) => {
                    let data;
                    if (typeof e.target.result === 'object') {
                        // 把Array Buffer转化为blob 如果是base64不需要
                        data = window.URL.createObjectURL(new Blob([e.target.result]));
                    }
                    else {
                        data = e.target.result;
                    }
    
                    // 上传成功后将图片地址赋值给裁剪框显示图片
                    this.$nextTick(() => {
                        this.option.img = data;
                        this.showImagePopover = true;
                    })
                };
                
                // 转化为base64
                // reader.readAsDataURL(imgTarget);
                // 转化为blob
                reader.readAsArrayBuffer(imgTarget);
                /* // 关键代码 */

                this.imgTargetInfo = {
                    fileName: imgTarget.name
                }
            },
            // 点击裁剪,这一步是可以拿到处理后的地址
            finish() {
                this.$refs.cropper.getCropBlob((data) => {
                    this.loading = true;
                    let formss = new FormData();
    
                    formss.append('file', data);
                    formss.append('fileName', this.imgTargetInfo.fileName);
                    formss.append('userId', this.userInfo.id);
    
                    // 这里是自己封装的一个axios方法,请求路径是下面Nodejs中的/upload
                    this.$ajax.photoUpload(formss).then((response) => {
                        console.log(response);
    
                        this.showImagePopover = false;
                        this.loading = false;
                        
                        if (response.code == 0) {
                            this.$toast("上传成功", {
                                location: 'bottom'
                            });
                        } else {
                            this.$toast(response.msg, {
                                location: 'bottom'
                            });
                        }
                    }).catch(error => {
                        this.$toast("上传图片出错", {
                            location: 'bottom'
                        });
    
                        this.showImagePopover = false;
                        this.loading = false;
                    })
                })
            }
        },
        mounted() {

        }
    }
</script>

<style scoped lang="less">
    .upload-photo {
        display: block;
        position: relative;
        margin-top: .3rem;
        padding: 8px 12px;
        background: #D0EEFF;
        border: 1px solid #99D3F5;
        overflow: hidden;
        color: #1E88C7;
        text-align: center;
        border-radius: 2px;
        
        input {
            position: absolute;
            font-size: 24px;
            right: 0;
            top: 0;
            opacity: 0;
        }
        
        &:hover {
            background: #AADFFD;
            border-color: #78C3F3;
            color: #004974;
            text-decoration: none;
        }
    }

    // 截图
    .cropper-content {
        .cropper {
            width: auto;
            height: 300px;
        }
    }
</style>

Nodejs接口代码

const express = require("express");
const multiparty = require('multiparty');
const router = express.Router();
const CONFIG = require("../config"); // ALIYUN OSS配置
let OSS = require('ali-oss');
let ossClient = new OSS(CONFIG.ALIYUN_OSS);

router.post("/upload", function (req, res) {
    let form = new multiparty.Form();
    form.parse(req, function(err, fields, file){
        if (err) {
            res.send({code: -1, msg: "服务异常"});
        } else {
            upload (res, fields, file);
        }
    });
});

async function upload (res, fields, file) {
    let userId = fields.userId[0];
    let size = file.file[0].size;

    if(userId === undefined || userId === "undefined"){
        res.send({code: 1001, msg: "参数异常"});
        return;
    }

    // 大小5MB
    if(size > 5 * 1024 * 1024){
        res.send({code: 1002, msg: "请选择5M以内的图片"});
        return false;
    }

    // 上传到OSS
    try {
        let fileName = userId + '-' + new Date().getTime() +'.jpg';
        let resOss = await ossClient.put(useTo +'/'+ fileName, file.file[0].path);
        let resUrl = resOss.url;

        if(resOss.res.statusCode == 200){
            res.send({code: 0, result: {url: fileName}});

            // 省略保存文件名和路径到数据库的代码...
        }else{
            res.send({code: -1, msg: "OSS上传图片服务异常"});
        }
    } catch (e) {
        res.send({code: -1, msg: "OSS上传图片服务异常"});
    }
}

module.exports = router;

猜你喜欢

发表评论

最新发布