上传图片并通过canvas添加水印

914 阅读2分钟

最近有个需求是需要将本地上传的图片添加水印,然后需要在详情中打开预览 上网上搜了一下,加水印的方法大都是通过canvas实现,所以自己折腾了一下成功满足了需求! 首先要有一个上传的按钮,思路我先讲一下,就是你先上传一张图片,action上配置好了上传地址,一般成功后会返回这个图片的信息,比如有的会返回在服务器上的绝对地址,用于上传完成后的预览,这个项目只返回了 fileID 需要通过,也就是 res.obj(至于为啥是这样我也不晓得哪个大神定义的字段,我属于接盘维护项目).拿到这个 fileID 需要通过 this.getImgUrl(res.obj)这个方法(主要是这个图片需要token所以要包装一下)生成绝对地址,这个不用纠结,你完全可以要求后端大佬直接给你绝对地址!!!获取到地址了就可以生成一张图片了撒!

<img style="display:none" ref="images" :src="currentImg"> 比如你用一个看不见的img盒子装这个图片用于后面的canvas来画图!当然可以 new image来实现,这样比较直观 然后来准备一个canvas

<canvas style="display:none" ref="canvas" id="canvas" width="800" height="800" ></canvas> 欧了,必要条件准备好了!开始着手画图了!下面这个 uploadSuccess 方法是图片上传成功的回调,不用介绍了吧?

uploadSuccess (res, file, fileList) {
            // res 是成功后会返回 res.obj ,通过这个生成完整图片
            if (res.code === "9999" && res.success) {
                // 这两个是水印的内容,用户名 + 当前时间 
                let text = this.$store.getters.userInfo.userName
                let textDate = this.currentTime
                //通过canvas偷偷的加水印
                // 当前上传图片的src
                this.currentImg = this.getImgUrl(res.obj)
                // 这是图片渲染完毕的一个回调
                this.$refs.images.onload = () => {
                    // 开始canvas了 看不懂可以去补补canvas,这个实在没法解释了
                    var context = this.$refs.canvas.getContext('2d');
                    this.$refs.canvas.width = 800;
                    this.$refs.canvas.height = 800;
                    // 看,就是这个方法 字面意思 0, 0 , 这是图片的坐标, 后面的800 是宽高
                    context.drawImage(this.$refs.images, 0, 0, 800, 800);
                    // 设置文字倾斜角度为 -25 度以及样式
                    context.rotate(-25 * Math.PI / 180);
                    // 水印字体
                    context.font = "14px microsoft yahei"
                    // 水印颜色
                    context.fillStyle = "rgba(238, 36, 5, 0.1)";
                    // 水印样式
                    context.textAlign = 'center';
                    context.textBaseline = 'Middle';
                    // 这个地方是为了让水印平铺整个canvas,单条水印賊丑
                    for(let i = (document.body.offsetHeight*0.5)*-1; i<800; i+=160) {
                        for(let j = 0; j<document.body.offsetHeight*1.5; j+=60) {
                        // 填充文字,x 间距, y 间距
                        context.fillText(text, i, j)
                        context.fillText(textDate, i + 84, j + 150)
                        }
                    }
                    // 到这一步,canvas已经画好了,你打开canvas的display:block就可以看到是不是成功了
                    // 这是上传的文件名字
                    this.fileName = file.name
                    // 这个方法是 将canvas转blob文件流,用于偷偷的发给后端保存水印图片
                    this.$refs.canvas.toBlob((blob) => {
                         // 这个是将 blob转成file文件流
                        let file = this.blobToFile(blob, this.fileName)
                        // 原始方法,賊尴尬
                        var formData = new FormData();
                        formData.append("file", file);
                        formData.append("fileName", 'file');
                        // 原始方法,賊尴尬
                        var request = new XMLHttpRequest();
                        request.open("POST", this.uploadUrl);
                        request.send(formData);
                        到这一步水印图片已经成功发给后端了,其实就是跟你第一次上传图片干的事情一样
                        request.onreadystatechange = () =>  {
                            this.uploadLoading.close()
                            // 这个readyState看不懂的麻烦去看看原生状态码
                            if (request.readyState === 4) { 
                            // 这个 status 看不懂的麻烦去看看原生状态码
                                if (request.status === 200) {
                                    好了,大功告成,获取到返回值
                                    this.waterFileId = JSON.parse(request.response).obj
                                    this.fileId = res.obj
                                    let imgItem = {
                                        waterFileId: this.waterFileId,
                                        srcUrl: this.getImgUrl(this.waterFileId),
                                        fileId: this.waterFileId,
                                        fileName: this.fileName.substring(0,this.fileName.indexOf(".")),
                                        documentType: this.fileName.split(".").pop().toLowerCase(),
                                        activityId: this.activityId,
                                        activityIdName: this.activityIdName,
                                        fileType: 2,
                                        bizOwnerId: this.bizid,
                                        tableName: this.tableName,
                                        roleNo: this.$store.getters.current_role.roleNo,
                                        zcgljb: this.$store.getters.mgtLevel
                                    }
                                    // 这个 srcList 就是上传好的带水印的图片数组了,用于当前预览
                                    this.srcList.push(imgItem)
                                    this.$emit("uploadSuccess", 'success')
                                } else {
                                    this.$emit("uploadSuccess", 'fail')
                                    console.log("Error", request.statusText);
                                }
                            }
                        };

                    })
                }
            } else {
                this.$toast.show(res.msg, 2000);
            }
        },
        // 这个方法也送给你们
        blobToFile (newBlob, fileName) {
            const files = new window.File(
                [newBlob],
                fileName, { type: 'image/png' }
            )
            return files
        }

白送一个预览的html <img v-for="item in srcList" :key="item.imgKey" @click="removeImg(item)" style="width: 80px; height: 80px;margin-right: 12px" :src="item.srcUrl" /> 这个就是 上面srcList 生成的图片预览的地方,很清楚了吧? 加油!星空不问赶路人!!!