暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Access to image at from origin has been blocked by CORS policy: No 'Access-Control-Allow-Origin 此图片来自微信公众平台,未经允许不可引用'

原创 浮游 2024-01-15
1829

问题背景

最近,在平台上发现很多图片无法展示,显示此图片来自微信公众平台,未经允许不可引用,分析原因后,发现是在发布文章时,富文本编辑器get图片,有跨域问题。

image.png

解决方案

在网上搜了很多方案,例如img.setAttribute(‘crossOrigin’, ‘anonymous’),发现无效,于是想到代理服务器。

思路:传递图片地址过去 让后端给转成buffer返回给前端,前端再上传二进制图片,到oss,获得链接

代理服务器非常简单,实现代码如下:

新建一个node项目

image.png

main.js const express = require('express') const fetch = require('node-fetch') const app = express(); app.use('/proxy', async (req, res) => { const imageUrl = req.query.url; try { const response = await fetch(imageUrl) const body = await response.buffer(); res.set('Content-Type', 'image/jpeg') res.send(body) } catch(e) { res.status(500).send('error') } }) app.listen(3333, () => { console.log('server is running on port 3333') })
package.json { "name": "img-proxy", "version": "1.0.0", "description": "", "main": "index.js", "type": "commonjs", "scripts": { "dev": "node main.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.18.2", "node-fetch": "^2.0.0" } }

然后在nuxt.config.js中配置代码

'/proxy': { target: 'http://localhost:3333/', changeOrigin: true },

在前端富文本中进行配置

<template> <div class="w-editor-box"> <div class="wEditor" id="wEditor"></div> <input class="hide" type="file" id="wordFile" @change="getWordFile" accept=".docx" /> </div> </template> <script> import { attachNoticesApi } from "@/apis"; export default { data() { return { editor: null, isHTML: false, loading: false, }; }, methods: { initEditor() { let _this = this; const E = window.wangEditor; const { $, BtnMenu } = E; class AlertMenu extends BtnMenu { constructor(editor) { const $elem = E.$( `<div class="w-e-menu"> <i class="cs-html"></i> </div>` ); super($elem, editor); } clickHandler() { this.tryChangeActive(); } tryChangeActive() { if (_this.isHTML) this.active(); else this.unActive(); } } this.editor = new E("#wEditor"); const editor = this.editor; editor.config.focus = false; editor.config.menus = [ "bold", "head", "italic", "underline", "strikeThrough", "list", "justify", "link", "quote", "image", "table", "code", ]; editor.highlight = hljs; editor.config.languageType = [ "SQL", "Shell Session", "Java", "JavaScript", "JSON", "Markdown", "TypeScript", "Plain text", "Html", "CSS", "Python", "XML", "Go", "Bash", "C", "C#", "C++", "Kotlin", "Lua", "PHP", "Ruby", ]; // 注册菜单 const menuKey = "alertMenuKey"; // 菜单 key ,各个菜单不能重复 editor.menus.extend("alertMenuKey", AlertMenu); editor.config.menus = editor.config.menus.concat(menuKey); editor.config.zIndex = 8; editor.config.customUploadImg = function (resultFiles, insertImgFn) { resultFiles.forEach((file) => { let formdata = new FormData(); formdata.append("file", file); attachNoticesApi(formdata).then((res) => { let imgUrl = res.data.operateCallBackObj; insertImgFn(imgUrl); }); }); }; editor.config.pasteTextHandle = function (pasteStr) { // 需要判断是否是纯文本,不是纯文本执行函数,是纯文本返回 // 验证图片中是否包含img标签,具有得到true否则得到false let containsImage = pasteStr.search(/<img /i) >= 0; // 存在图片就执行 if (containsImage) { // 打开loading _this.pasteTextFulfill = true; _this.disposePasteImg(pasteStr).then((res) => { // 将内容追加上 pasteStr = res editor.cmd.do('insertHTML', res) // 关闭loading _this.pasteTextFulfill = false; }) return ''; }else { return pasteStr; } }; editor.config.onchange = function (newHtml) { let _text = editor.txt.text(); if (_this.isHTML) { newHtml = _text _this.$emit("setValue", newHtml, _text, "richtxt"); }; editor.create(); }, displayResult(result) { let _editor = this.editor; let _source = _editor.txt.html() + result.value; _editor.txt.html(_source); wordFile.value = ""; this.loading = false; }, async uploadBase64Image(base64Image, mime) { const formdata = new FormData(); const _file = this.base64ToBlob(base64Image, mime); formdata.append("file", _file); let { data } = await attachNoticesApi(formdata); return data.operateCallBackObj; }, base64ToBlob(base64, mime) { mime = mime || ""; const sliceSize = 1024; const byteChars = window.atob(base64); const byteArrays = []; for ( let offset = 0, len = byteChars.length; offset < len; offset += sliceSize ) { const slice = byteChars.slice(offset, offset + sliceSize); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } return new Blob(byteArrays, { type: mime }); }, // 处理粘贴的图片,传递的参数是粘贴来的数据 disposePasteImg(pasteStr) { let _this = this; return new Promise(function (resolve) { // 用于计数图片数量 let imgNum = 0; //匹配图片 let imgReg = /<img.*?(?:>|\/>)/gi; //匹配src属性 // eslint-disable-next-line no-useless-escape let srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i; // 提取图片连接 if (pasteStr.match(imgReg)) { pasteStr.replace(imgReg, function (txt) { return txt.replace(srcReg, function (src) { let img_src = src.match(srcReg)[1]; //正则把?x-oss-process后面的都去掉 img_src = img_src.replace(/\?.*/i, ""); // 查找到一个图片地址就讲图片数量加1 imgNum++; // 将图片转成图片文件对象并上传得到图片地址,传递的参数是图片地址 _this.imgUrlSwitchBlob(img_src).then((res) => { /** * 得到图片地址进行替换并将替换之后的文本返回渲染 */ pasteStr = pasteStr.replace(img_src, res); // 替换之后将图片数量减1 imgNum--; // 只有图片数量变成0的时候才会进行返回 if (imgNum == 0) { resolve(pasteStr); } }); }); }); } else { resolve(pasteStr); } }); }, /** * @函数名称: 将图片地址转成文件对象 * @返回值:图片地址 * @描述: 接受的参数是图片的全地址路径,在函数中调用upPasteImg函数上传图片得到图片路径并返回 * @其它: 使用Promise处理异步问题 * @param {String} param 图片地址 */ imgUrlSwitchBlob(param) { let _this = this; return new Promise(function (resolve) { fetch(`/proxy?url=${param}`) .then((res) => { if (res.ok) return res.blob(); }) .then((blob) => { let suffix = param.substring(param.lastIndexOf(".") + 1); //获取后缀 // 设置图片名称及后缀 const crypto = window.crypto || window.webkitCrypto || window.mozCrypto || window.oCrypto || window.msCrypto; let _Math = crypto.getRandomValues(new Uint32Array(1)); const key = new Date().getTime() + _Math.toString().substr(0, 5) + "." + suffix; // 创建图片对象 let image = new Image(); image.src = param; image.onload = () => { _this.upPasteImg(key, blob).then((res) => { resolve(res); }); } }); }); }, /** * @函数名称: 上传粘贴的图片 * @返回值: 上传得到的图片地址 * @其它: 使用Promise处理异步问题 * @param {String} key 文件名称 * @param {Object} file 文件对象 */ upPasteImg(key, file) { let _this = this; let formdata = new FormData(); formdata.append("file", file); return new Promise(function (resolve) { attachNoticesApi(formdata).then((res) => { let imgUrl = res.data.operateCallBackObj; resolve(imgUrl); }); }); }, }, mounted() { this.initEditor(); }, }; </script>

效果展示

传过去图片地址,前端根据后端返回的blob,上传图片,最终完美解决

image.png

上传二进制

image.png

返回图片地址

最后修改时间:2024-01-15 16:43:51
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论