使用Vue進(jìn)行圖片壓縮的實(shí)現(xiàn)方案和對(duì)比
在 Vue 項(xiàng)目中處理用戶上傳的大圖片時(shí),通過(guò)前端壓縮減小圖片體積是優(yōu)化上傳體驗(yàn)的有效方式。以下為具體實(shí)現(xiàn)方案及相關(guān)說(shuō)明:
一、圖片壓縮方案對(duì)比
不同壓縮方案在體積縮減、兼容性、耗時(shí)等方面各有特點(diǎn),具體如下:
| 方案 | 體積縮減 | 兼容性 | 耗時(shí) | 備注 |
|---|---|---|---|---|
| Canvas 縮放 | 60-85% | 99% | 中 | 本文主方案 |
| WebP encode | 額外-30% | 92% | 高 | 需服務(wù)端配合回退 JPG |
| WebAssembly | 70-90% | 92% | 高 | mozJPEG/avif-js,包大 |
| 服務(wù)端壓縮 | 0% | 100% | 低 | 前端仍要先傳原圖 |
二、核心實(shí)現(xiàn):Canvas 縮放壓縮
利用 HTML5 的 Canvas API,通過(guò)調(diào)整圖片尺寸和質(zhì)量實(shí)現(xiàn)壓縮,是兼容性和效果平衡較好的方案。
1. 壓縮工具函數(shù)
核心思路是利用 HTML5 的 Canvas API 對(duì)圖片進(jìn)行壓縮,主要通過(guò)調(diào)整圖片尺寸和質(zhì)量來(lái)實(shí)現(xiàn)。
/**
* 圖片壓縮工具
* @param file 原始圖片文件
* @param options 壓縮選項(xiàng)
* @returns 壓縮后的圖片文件
*/
export const compressImage = async (
file: File,
options: {
maxWidth?: number; // 最大寬度
maxHeight?: number; // 最大高度
quality?: number; // 圖片質(zhì)量 0-1
} = {}
): Promise<File | null> => {
// 默認(rèn)配置
const {
maxWidth = 1200,
maxHeight = 1200,
quality = 0.8
} = options;
return new Promise((resolve) => {
// 創(chuàng)建圖片對(duì)象
const img = new Image();
const objectUrl = URL.createObjectURL(file);
img.src = objectUrl;
img.onload = () => {
URL.revokeObjectURL(objectUrl);
// 計(jì)算壓縮后的尺寸(保持比例)
let width = img.width;
let height = img.height;
// 如果圖片尺寸超過(guò)最大限制,按比例縮小
if (width > maxWidth || height > maxHeight) {
const ratio = Math.min(maxWidth / width, maxHeight / height);
width = width * ratio;
height = height * ratio;
}
// 創(chuàng)建Canvas元素
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
// 繪制圖片到Canvas
const ctx = canvas.getContext('2d');
if (!ctx) {
resolve(null);
return;
}
ctx.drawImage(img, 0, 0, width, height);
// 將Canvas內(nèi)容轉(zhuǎn)換為Blob
canvas.toBlob(
(blob) => {
if (!blob) {
resolve(null);
return;
}
// 轉(zhuǎn)換為File對(duì)象
const compressedFile = new File(
[blob],
file.name,
{ type: blob.type, lastModified: Date.now() }
);
resolve(compressedFile);
},
file.type || 'image/jpeg',
quality
);
};
img.onerror = () => {
URL.revokeObjectURL(objectUrl);
resolve(null);
};
});
};
2. Vue3 組件中使用示例
<template>
<div class="image-upload">
<input
type="file"
accept="image/*"
@change="handleFileChange"
class="file-input"
/>
<div v-if="previewUrl" class="preview">
<img :src="previewUrl" alt="預(yù)覽圖" class="preview-img" />
<p>原始大小: {{ originalSize }}KB</p>
<p v-if="compressedSize">壓縮后大小: {{ compressedSize }}KB</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { compressImage } from '@/utils/imageCompressor';
const previewUrl = ref('');
const originalSize = ref(0);
const compressedSize = ref(0);
const handleFileChange = async (e: Event) => {
const input = e.target as HTMLInputElement;
if (!input.files || input.files.length === 0) return;
const file = input.files[0];
// 顯示原始文件大小
originalSize.value = Math.round(file.size / 1024);
// 如果文件小于500KB,不壓縮直接使用
if (file.size < 500 * 1024) {
previewUrl.value = URL.createObjectURL(file);
compressedSize.value = originalSize.value;
return;
}
// 壓縮圖片
const compressedFile = await compressImage(file, {
maxWidth: 1000,
maxHeight: 1000,
quality: 0.7
});
if (compressedFile) {
previewUrl.value = URL.createObjectURL(compressedFile);
compressedSize.value = Math.round(compressedFile.size / 1024);
// 這里可以將compressedFile上傳到服務(wù)器
// await uploadToServer(compressedFile);
}
};
</script>
<style scoped>
.image-upload {
display: flex;
flex-direction: column;
gap: 1rem;
}
.file-input {
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
}
.preview {
margin-top: 1rem;
}
.preview-img {
max-width: 100%;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
</style>
三、服務(wù)端二次校驗(yàn)(Node 示例)
router.post('/upload', multer().single('file'), async (req, res) => {
const { buffer, mimetype, size } = req.file;
if (size > 1024 * 1024) return res.status(413).send('too big');
if (!['image/jpeg', 'image/webp'].includes(mimetype)) return res.status(400).send('bad type');
// 計(jì)算 SSIM 或 Magic Number 防止“偽造壓縮”
const metadata = await sharp(buffer).metadata();
if (metadata.width! > 1600) return res.status(400).send('width overflow');
// 真正落盤(pán)
await putToOSS(buffer, mimetype);
res.json({ url });
});四、注意事項(xiàng)
壓縮會(huì)損失一定畫(huà)質(zhì),需根據(jù)業(yè)務(wù)場(chǎng)景平衡畫(huà)質(zhì)與文件大小;
對(duì)于幾十 MB 的超大圖片,可考慮分階段壓縮(先大幅縮小尺寸,再調(diào)整質(zhì)量);
服務(wù)端必須進(jìn)行二次校驗(yàn),防止前端繞過(guò)壓縮邏輯上傳超大文件。
到此這篇關(guān)于使用Vue進(jìn)行圖片壓縮的實(shí)現(xiàn)方案和對(duì)比的文章就介紹到這了,更多相關(guān)Vue圖片壓縮內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue組件中的data必須是一個(gè)function的原因淺析
這篇文章主要介紹了Vue組件中的data必須是一個(gè)function的原因淺析,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09
vue3.0 CLI - 2.4 - 新組件 Forms.vue 中學(xué)習(xí)表單
這篇文章主要介紹了vue3.0 CLI - 2.4 - 新組件 Forms.vue 中學(xué)習(xí)表單的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2018-09-09
vue初嘗試--項(xiàng)目結(jié)構(gòu)(推薦)
這篇文章主要介紹了vue初嘗試--項(xiàng)目結(jié)構(gòu)的相關(guān)知識(shí),需要的朋友可以參考下2018-01-01
vue金額格式化保留兩位小數(shù)的實(shí)現(xiàn)
這篇文章主要介紹了vue金額格式化保留兩位小數(shù)的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
基于vue實(shí)現(xiàn)頁(yè)面滾動(dòng)加載的示例詳解
頁(yè)面內(nèi)容太多會(huì)導(dǎo)致加載速度過(guò)慢,這時(shí)可考慮使用滾動(dòng)加載即還沒(méi)有出現(xiàn)在可視范圍內(nèi)的內(nèi)容塊先不加載,出現(xiàn)后再加載,所以本文給大家介紹了基于vue實(shí)現(xiàn)頁(yè)面滾動(dòng)加載的示例,需要的朋友可以參考下2024-01-01
vue?element-ui動(dòng)態(tài)橫向統(tǒng)計(jì)表格的實(shí)現(xiàn)
這篇文章主要介紹了vue?element-ui動(dòng)態(tài)橫向統(tǒng)計(jì)表格的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
elementUI動(dòng)態(tài)表單?+?el-select?按要求禁用問(wèn)題
這篇文章主要介紹了elementUI動(dòng)態(tài)表單?+?el-select?按要求禁用問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10

