component-后端返回图片(数据)前端进行复制到剪切板
1.前言
-
Base64编码:将图片转换为Base64编码的字符串,然后通过HTTP协议传输到前端。前端接收到Base64字符串后,可以通过JavaScript将其解码并显示为图片。这种方式适合小图片,如logo或验证码,因为Base64编码后的字符串较长,可能会影响页面加载速度和性能12。
-
URL地址:将图片上传到服务器,获取图片的URL地址后,将该URL地址传输到前端。前端通过设置
<img>
标签的src
属性为该URL地址来显示图片。这种方式适用于大图片,因为不需要将图片数据直接传输到前端,而是通过URL地址访问服务器上的图片资源。 -
字节流:通过HTTP响应的输出流直接将图片以二进制形式传输到前端。前端接收到图片的字节流后,可以直接在
<img>
标签的src
属性中设置data:image/png;base64,
前缀,并附加图片的Base64编码字符串来显示图片。这种方式适用于需要实时处理或展示图片的场景
2. URL地址形式
<template>
<div class="copyAll">
<a-form class="search">
<!-- 复制二维码 -->
<a-form-item label="二维码">
<div class="codeAll">
<div class="codeCopy">
<img src="https://img0.baidu.com/it/u=3383325011,2141463052&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1424" alt="">
</div>
<a-button type="primary" @click="copyImageToClipboard('https://img0.baidu.com/it/u=3383325011,2141463052&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1424')">复制二维码</a-button>
</div>
</a-form-item>
</a-form>
</div>
</template>
<script>
export default {
name: 'Copy',
data () {
return {
}
},
methods: {
// 通过 Canvas,图像绘制到画布上,然后将其转换为支持的格式(如 PNG),复制到剪贴板。
async copyImageToClipboard(imageUrl) {
// 异步请求
const response = await fetch(imageUrl);
const contentType = response.headers.get('Content-Type') || 'image/png';
const isSupportedImageType = contentType.startsWith('image/');
if (!isSupportedImageType) {
throw new Error('Unsupported image type');
}
const blob = await response.blob(); // 获取图像的 Blob 对象
const imgElement = new Image(); // 创建 Image 对象
imgElement.src = URL.createObjectURL(blob); // 将 Blob 转为 URL
return new Promise((resolve, reject) => {
imgElement.onload = async () => {
const canvas = document.createElement('canvas'); // 创建 Canvas
const ctx = canvas.getContext('2d'); // 获取 Canvas 上下文
if (contentType === 'image/gif') {
// 处理 GIF,直接使用 GIF Blob
const item = new ClipboardItem({ 'image/gif': blob }); // 使用 GIF 格式
await navigator.clipboard.write([item]); // 写入剪贴板
resolve();
} else {
// 处理 JPG/JPEG/PNG,转换为 PNG
canvas.width = imgElement.width; // 设置宽度
canvas.height = imgElement.height; // 设置高度
ctx.drawImage(imgElement, 0, 0); // 将图像绘制到 Canvas
// 转换为 Blob,使用 'image/png' 作为 MIME 类型
canvas.toBlob(async (newBlob) => {
const item = new ClipboardItem({ 'image/png': newBlob }); // 使用 PNG 格式
await navigator.clipboard.write([item]); // 写入剪贴板
resolve(); // Promise 成功
}, 'image/png');
}
};
imgElement.onerror = reject; // 处理错误
});
}
}
}
</script>
<style lang="less" scoped>
// 全部
.copyAll{
padding: 30px;
}
// 二维码
.codeAll{
display: flex;
flex-direction: row;
width: 220px;
align-items: center;
}
// 二维码展示
.codeCopy{
height: 150px;
width: 110px;
// background-color: red;
img{
height: 100%;
width: 100%;
}
}
// 按钮间距
/deep/.ant-btn-primary{
margin-left: 10px !important;
}
</style>
3.后端返回Base64编码的字符串 进行复制图片
3.1 具体代码
<template>
<div>
<img :src="imgSrc" alt="" class="qrcode">
<button @click="copyImage">复制图片</button>
</div>
</template>
<script>
export default {
data() {
return {
base64Image: 'iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAIAAAAP3aGbAAAL00lEQVR42u3cQY6kMBBE0br/pekztESSEfb7a8RQJv08C9S/R5JK+lkCScCSJGBJApYkAUuSgCUJWJIELEkCliRgSRKwJAlYkoAlScCSJGBJApYkAUuSgCUJWJJUBtavpLee/7/3WXupYc+z9ZzT83Dq82/tR2ABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWPMf5rVsjBYop9etZT0dhDkfVAMLWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABay8BUp7/rS2oGlZ57QDEljAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMBKXaDkgd7aALdBkwY0sIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhaw7garBQgb4xtQfuU9Yd2wH4EFLGABC1heGLCAZf6BBSxgAQtYFghYwAIWsLwwYAHL/AMLWMACFrAsELCABaybwGoZLNe7/obrT4IeWK53PbCABSzXux5YwAKW610PLGC53vXAAhawXO96YAELWK53PbCA5XrXA+tEsNp7a0EnXsyXg3vb75o+mB/N7VlgAQtYwAIWsIAFLAELWMACFrCABSxgAQtYwAIWsAQsYAELWMACFrCABawDwUrbSNOD1fIha9o6pwHaAnHLAVz/pTuwgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMAC1jJYW4OeNlhbIKbd/7aNlAZxGqzAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMDKHsQWKFvgTh7QL9t6X+33+XJugQUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCANX8fg/vu79qCLw3WtPfYPs/AAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMDKeJEtG3v6d9kA39xnq1NBBxawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACVsagn7rxnrBOhSYNgjRwp9cBWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtY3w7Q1vVbAKWtWzvcWwdDCxzAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMDqBOvUgWsHd+v+7QfP9L/bAvqXzw8sYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABay9jdTyPC33f8JqWYf297K1v4AFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawTgdreiG2XnwLNG+tz0kDPbEOLXPSvs7AAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMCaefFbgzU9cC0bb+v3bm2MLeitA7CABSxgAQtYwAIWsIAFLGDZqNYBWMACFrCABSxgAQtYwAIWsGxU6wAsYAELWHeD1fKBZfuGb4cv7SDZ+r1vPefWgZEAHLCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsPY2dtpGSoMybf0TNkbCvmifW2ABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYGW/mOnBSrvPNDRbQKSBu/WcaeufDB+wgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMAC1olgtQ9KywaeHqD297V1YNwGesL6AAtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGDdBFb7QGwBcSoEW1C2gN4OIrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwALWpWC1bNStQU87GNI26tZ73zoYbpgrYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABawTwWoZ6LcW+tSBS3vOtIMz7YBsgRhYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1idgzL9PL+StiBIg6nld23tr0bIgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrBuAmt6g23dp+UFp63/FtBb92mf82TogQUsYAELWMACFrCABSxgAQtYwAKWjQcsYAELWMACFrCABSxgAQtYwLLxgAUsYAELWMA6ESwfNH6zAbY+mJx+zrQDpv1ASviwM/rDUWABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAWsULBu+7BwerBaNtLW82/dJ+09ps0PsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawMv6A36kfIk4Dl7Zhtuan/eBJ+7ATWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtY2WA1LlDyILYM9NZ795yZvwtYwAKW5wQWsIAFAmABC1jAAhawgAUsYHlOYAELWCAAFrCABSxgAQtYwAKW5wTWWWBNP880EGkbI+1gSFv/Z6n25wcWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAlYnWGnr0LLBtuBrmcO0g6ExYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgffBjwjbANDQ28C7QafuifW6BBSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMACFrCABSxgAQtYwAIWsICVXdrGToN+6wBon580aNqhBxawgAUsYAELWMACFrCABSxgAQtYwAIWsIAFLGABC1jAAhawgAUsYAELWMC6FSxJApYkAUsSsCQJWJIELEnAkiRgSRKwJAFLkoAlCViSBCxJApYkYEkSsCQJWJKAJU'
}
},
computed: {
imgSrc() {
return `data:image/jpg;base64,${this.base64Image}`
}
},
methods: {
async copyImage() {
try {
// 将Base64字符串转换为Uint8Array
// 解码Base64字符串
const binaryString = atob(this.base64Image);
const len = binaryString.length;
//二进制字符串转换为一个Uint8Array数组。
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// 将Uint8Array转换为Blob对象
const blob = new Blob([bytes], { type: 'image/png' });
// 创建URL对象
const url = URL.createObjectURL(blob);
// 将Blob对象对象复制到剪贴板
await navigator.clipboard.write([
new ClipboardItem({
'image/png': blob
})
]);
console.log('图片已复制到剪贴板');
} catch (err) {
console.error('复制图片时出错:', err);
}
}
}
}
</script>
3.2代码详解
3.2.1Base64编码的字符串解码为二进制字符串
atob
函数是btoa
函数的逆操作。btoa
用于将二进制数据转换为Base64编码的字符串,而atob
则用于将Base64编码的字符串解码回原始的二进制数据。
const binaryString = atob(this.baseSrc);
this.baseSrc = "SGVsbG8gd29ybGQ=";
举例
const binaryString = atob(this.baseSrc);
//解码后
"Hello world"
3.2.2二进制字符串转换为一个Uint8Array
数组
const binaryString = "01010101";
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
console.log(bytes); // 输出: Uint8Array(8) [48, 49, 48, 49, 48, 49, 48, 49]
创建一个Uint8Array
数组:
const bytes = new Uint8Array(len);
Uint8Array
是一种类型化数组,用于表示8位无符号整数,范围从0到255。
字符转换为对应的ASCII码:
bytes[i] = binaryString.charCodeAt(i);
循环中,binaryString.charCodeAt(i)
方法用于获取二进制字符串中第i
个字符的ASCII码,并将其赋值给bytes
数组中的第i
个元素。
举例
const binaryString = "01010101";
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
console.log(bytes); // 输出: Uint8Array(8) [48, 49, 48, 49, 48, 49, 48, 49]
binaryString
中的每个字符都被转换为其对应的ASCII码,并存储在bytes
数组中。
3.2.3 将Uint8Array转换为Blob对象
在JavaScript中,Blob
(Binary Large Object)对象表示不可变的、原始数据的类文件对象。它通常用于处理二进制数据。
-
将二进制数据保存到本地文件系统。
-
将二进制数据上传到服务器。
-
在网页中显示图像或视频。
Blob
构造函数来创建一个Blob对象。Blob
构造函数接受两个参数:
-
数组:第一个参数是一个数组,数组中的每个元素可以是任意类型的数据,比如字符串、ArrayBuffer、ArrayBufferView、Blob等。这些数据会被合并成一个Blob对象。
-
选项对象(可选):第二个参数是一个可选的对象,用于指定Blob的类型。例如,如果创建的是一个图像文件,可以将类型设置为
'image/png'
。
// 创建一个包含文本数据的Blob对象
const blob = new Blob(['Hello, world!'], { type: 'text/plain' });
// 创建一个包含图像数据的Blob对象
const imageBlob = new Blob([imageData], { type: 'image/png' });
3.2.4创建URL对象
// 创建URL对象
const url = URL.createObjectURL(blob);
URL.createObjectURL
方法接受一个Blob
对象作为参数,并返回一个临时的URL。这个URL指向的是内存中的对象,而不是文件系统中的实际文件。这个URL是唯一的,并且只在创建它的窗口或标签页中有效。
-
文件上传和下载:在文件上传或下载的场景中,可以使用
Blob
对象来表示文件,然后通过URL.createObjectURL
生成一个临时的URL,用于在网页中显示或下载文件。 -
图像预览:在处理图像上传时,可以使用
Blob
对象和URL.createObjectURL
来预览图像,而不需要将图像保存到服务器。 -
视频播放:在处理视频文件时,可以使用
Blob
对象和URL.createObjectURL
来在网页中播放视频。
// 假设有一个Blob对象
const blob = new Blob(['Hello, world!'], { type: 'text/plain' });
// 创建一个表示该Blob对象的URL
const url = URL.createObjectURL(blob);
// 使用这个URL,例如在<img>标签中显示图像
const img = document.createElement('img');
img.src = url;
document.body.appendChild(img);
// 当不再需要这个URL时,应该释放内存
URL.revokeObjectURL(url);
3.2.5Blob对象复制到剪贴板
将一个图像(以Blob对象的形式)复制到剪贴板。navigator.clipboard.write
方法,浏览器提供的一个API,用于将数据写入剪贴板。navigator.clipboard
API在大多数现代浏览器中都是可用的,但在某些情况下可能需要用户授权。
-
Blob对象:
blob
是一个Blob对象,代表一个不可变的、原始数据的类文件对象。在这个例子中,它代表了一个图像文件。 -
ClipboardItem:
ClipboardItem
是一个构造函数,用于创建一个表示剪贴板内容的对象。在这个例子中,它创建了一个包含图像数据(以PNG格式)的剪贴板项。 -
navigator.clipboard.write:
navigator.clipboard.write
是一个异步方法,用于将数据写入剪贴板。它接受一个包含一个或多个ClipboardItem
对象的数组作为参数。
async function copyImageToClipboard(blob) {
try {
await navigator.clipboard.write([
new ClipboardItem({
'image/png': blob
})
]);
console.log('Image copied to clipboard');
} catch (err) {
console.error('Failed to copy image to clipboard', err);
}
}