Skip to content

Commit

Permalink
Merge pull request #22 from ATQQ/feature/compress
Browse files Browse the repository at this point in the history
Feature/compress
  • Loading branch information
ATQQ authored Mar 9, 2024
2 parents 1288ce2 + da4fc64 commit b64ea19
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 26 deletions.
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default antfu({
}, {
ignores: [
'.github/workflows/client.yml',
'packages/client/public/UPNG.js',
],
}, {
rules: {
Expand Down
2 changes: 0 additions & 2 deletions packages/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
<title>七牛云OSS图床 | 粥里有勺糖</title>
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
<script>LA.init({ id: "KK6tcRYp7qnv7PCY", ck: "KK6tcRYp7qnv7PCY", hashMode: true })</script>
<script
src="https://polyfill.alicdn.com/polyfill.min.js?features=es2019%2Ces2018%2Ces2017%2Ces5%2Ces6%2Ces7%2Cdefault"></script>
</head>

<body>
Expand Down
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"qiniu": "^7.11.0",
"qiniu-js": "^3.4.1",
"spark-md5": "^3.0.2",
"upng-js": "^2.1.0",
"vue": "^3.4.15",
"vue-router": "^4.2.5"
},
Expand Down
9 changes: 6 additions & 3 deletions packages/client/src/components/ImageList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const checkInfo = (image: IImage) => {
<li>链接:<a target="_blank" href=${image.url}>${image.url}</a></li>
<li>上传时间:${image.date && formatDate(image.date)}</li>
<li>大小:${image.size ? formatSize(image.size) : '未知'}</li>
${image.originSize ? `<li>压缩前大小:${formatSize(image.originSize)}</li>` : ''}
</ul>
</div>`
})
Expand Down Expand Up @@ -56,7 +57,8 @@ const showImage = computed(() => {
</a>
</span>
<span style="width: 160px;" class="right">
<el-button v-if="image.size" type="warning" link>{{ formatSize(image.size) }}</el-button>
<el-button v-if="image.size" :type="image.originSize ? 'success' : 'warning'" link>{{ formatSize(image.size)
}}</el-button>
<el-button type="primary" link @click="checkInfo(image)">🔍</el-button>
<el-button type="primary" link @click="copyAddress(image.url)">url</el-button>
<el-button type="success" link @click="copyMdAddress(image.url)">markdown</el-button>
Expand Down Expand Up @@ -85,7 +87,7 @@ ul.el-upload-list {
display: flex;
justify-content: space-between;
.left{
.left {
flex: 1;
}
Expand Down Expand Up @@ -127,4 +129,5 @@ ul.el-upload-list {
li {
word-break: break-all;
}
}</style>
}
</style>
27 changes: 17 additions & 10 deletions packages/client/src/components/ImageUpload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import { UploadFilled } from '@element-plus/icons-vue'
import { computed, ref, watch } from 'vue';
import { ElMessage, type UploadInstance, type UploadProps, type UploadUserFile } from 'element-plus'
import { compressImage, uploadFile } from '../utils/qiniu'
import { uploadFile } from '../utils/qiniu'
import { useFocus } from '@vueuse/core';
import { useConfigStore, useImageStore } from '@/store'
import { storeToRefs } from 'pinia';
import { formatSize } from '@/utils/stringUtil';
import { useUploadConfig } from '@/composables';
import { compressImage } from '@/utils/file';
const imageStore = useImageStore()
const configStore = useConfigStore()
Expand All @@ -23,6 +24,7 @@ const handleChange: UploadProps['onChange'] = (_, uploadFiles) => {
return ok
})
}
const cacheConfig = useUploadConfig()
watch(files, async () => {
for (const file of files.value) {
Expand All @@ -33,12 +35,14 @@ watch(files, async () => {
if (!file.raw) {
continue
}
compressImage(file.raw,{
noCompressIfLarger: true
}).then(v=>{
console.log(v.dist, formatSize(v.dist.size));
})
uploadFile(file.raw, qiniu.value, {
let fileRaw = file.raw
if (cacheConfig.value.compressImage) {
// TODO: 未来开放自定义调整
// 采取自动压缩策略
fileRaw = await compressImage(file.raw) as any
}
uploadFile(fileRaw, qiniu.value, {
process(percent) {
file.percentage = percent
if (percent === 100) {
Expand All @@ -54,8 +58,9 @@ watch(files, async () => {
imageStore.addImage({
url: v,
name: file.name || 'image',
file: file.raw,
size: file.raw?.size || 0,
file: fileRaw,
size: fileRaw?.size || 0,
originSize: fileRaw === file.raw? 0 : file.raw?.size,
})
}).catch(err => {
ElMessage.error(err)
Expand Down Expand Up @@ -123,6 +128,7 @@ watch($pasteArea, () => {
const { focused } = useFocus($pasteArea)
const pasteText = computed(() => focused.value ? '现在你可以粘贴了' : '你也可以点击此处,然后粘贴你要上传的图片')
</script>

<template>
<div class="upload-wrapper">
<el-upload accept="image/*" v-model:file-list="files" ref="uploadRef" :on-change="handleChange" drag multiple
Expand All @@ -140,6 +146,7 @@ const pasteText = computed(() => focused.value ? '现在你可以粘贴了' : '
</el-upload>
</div>
</template>

<style lang="scss" scoped>
.upload-wrapper {
position: relative;
Expand Down
22 changes: 18 additions & 4 deletions packages/client/src/components/UploadTool.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script setup lang="ts">
import { watch } from 'vue'
import { useUploadConfig } from '@/composables'
import { useImageStore } from '@/store'
Expand All @@ -15,17 +15,24 @@ watch(() => success.value.length, () => {
}
})
</script>

<template>
<div class="tool-wrapper">
<span class="autoCopy">
<el-switch v-model="cacheConfig.autoCopy" inline-prompt active-text="自动复制" inactive-text="关闭自动复制" />
<el-select style="width: 100px;" v-if="cacheConfig.autoCopy" v-model="cacheConfig.copyType" placeholder="选择复制类型"
size="small">
<el-switch v-model="cacheConfig.autoCopy" inline-prompt active-text="自动复制" inactive-text="自动复制" />
<el-select style="width: 100px;" v-if="cacheConfig.autoCopy" v-model="cacheConfig.copyType"
placeholder="选择复制类型" size="small">
<el-option label="链接" value="url" />
<el-option label="Markdown" value="markdown" />
<template #empty></template>

<template #prefix></template>
</el-select>
</span>
<span class="compress">
<el-switch v-model="cacheConfig.compressImage" inline-prompt active-text="压缩" inactive-text="压缩" />
<!-- <el-switch v-if="cacheConfig.compressImage" class="preview-switch" v-model="cacheConfig.compressPreview" inline-prompt active-text="预览" inactive-text="关闭预览" /> -->
</span>
</div>
</template>

Expand All @@ -36,6 +43,12 @@ watch(() => success.value.length, () => {
padding: 0 10px;
display: flex;
justify-content: center;
>span{
margin: 2px 6px;
}
.el-switch{
--el-switch-off-color: #ff4949;
}
}
.autoCopy {
Expand All @@ -46,4 +59,5 @@ watch(() => success.value.length, () => {
margin-left: 10px;
}
}
</style>
3 changes: 2 additions & 1 deletion packages/client/src/composables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export function useIsMobile() {
})
}

const defaultUploadConfig = { autoCopy: true, copyType: 'markdown', pageSize: 20, compressImage: true, compressPreview: true }
export function useUploadConfig() {
const cacheConfig = useLocalStorage('uploadConfig', { autoCopy: true, copyType: 'markdown', pageSize: 20 })
const cacheConfig = useLocalStorage('uploadConfig', defaultUploadConfig)
return cacheConfig
}
4 changes: 4 additions & 0 deletions packages/client/src/shims-vue.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 第三方库的类型定义
// interface Window {
// UPNG: any
// }
9 changes: 8 additions & 1 deletion packages/client/src/store/modules/imageStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { defineStore } from 'pinia'

export interface IImage { url: string, name: string, file?: File, date?: number, size: number }
export interface IImage {
url: string
name: string
file?: File
date?: number
size: number
originSize?: number
}

const imgStore = defineStore('imgStore', {
state: () => ({
Expand Down
94 changes: 94 additions & 0 deletions packages/client/src/utils/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// @ts-expect-error
import UPNG from 'upng-js'
interface CompressOptions {
/**
* 压缩质量(0-100)
* @default 80
*/
quality?: number
/**
* 压缩后更大是否使用原图
* @default true
*/
noCompressIfLarger?: boolean
/**
* 压缩后的新宽度
* @default 原尺寸
*/
width?: number
/**
* 压缩后新高度
* @default 原尺寸
*/
height?: number
}
async function compressImage(file: File, ops: CompressOptions = {}) {
const { width, height, quality = 80, noCompressIfLarger } = ops
const isPng = await isPNG(file)
let newFile: File | null = null
if (isPng) {
const arrayBuffer = await getBlobArrayBuffer(file)
const decoded = UPNG.decode(arrayBuffer)
const rgba8 = UPNG.toRGBA8(decoded)
const compressed = UPNG.encode(rgba8, width || decoded.width, height || decoded.height, convertQualityToBit(quality))
newFile = new File([compressed], file.name, { type: 'image/png' })
}

if (!newFile) {
return file
}

if (!noCompressIfLarger) {
return newFile
}

return file.size > newFile.size ? newFile : file
}

function getBlobArrayBuffer(file: Blob): Promise<ArrayBuffer> {
return file.arrayBuffer()
}

async function isPNG(file: File) {
const arraybuffer = await getBlobArrayBuffer(file.slice(0, 8))
return signatureEqual(arraybuffer, [137, 80, 78, 71, 13, 10, 26, 10])
}

function signatureEqual(source: ArrayBuffer, signature: number[]) {
const array = new Uint8Array(source)
for (let i = 0; i < signature.length; i++) {
if (array[i] !== signature[i]) {
return false
}
}
return true
}

function getImageDimensions(file: File): Promise<{ width: number, height: number }> {
return new Promise ((resolve) => {
const img = new Image()
img.onload = function () {
resolve({ width: img.width, height: img.height })
}
img.onerror = function () {
resolve({ width: 0, height: 0 })
}
img.src = URL.createObjectURL(file)
})
}

function convertQualityToBit(quality: number): number {
let bit = 0
if (quality > 100 || quality < 0) {
bit = 0
}
else {
bit = !quality ? 0 : quality * 256 * 0.01
}
return bit
}

export {
compressImage,
getImageDimensions,
}
5 changes: 0 additions & 5 deletions packages/client/src/utils/qiniu.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as qiniu from 'qiniu-js'
import type { CompressOptions } from 'qiniu-js/esm/utils/compress'
import { getFileMd5Hash } from './stringUtil'
import type { QiNiuConfig } from '@/store/modules/configStore'

Expand Down Expand Up @@ -50,11 +49,7 @@ async function generateNewFileKey(file: File, prefix = 'mdImg', scope = 'sugar')
return `${prefix}/${scope}/${md5}`
}

async function compressImage(file: File, ops: CompressOptions) {
return qiniu.compressImage(file, ops)
}
export {
uploadFile,
generateNewFileKey,
compressImage,
}
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b64ea19

Please sign in to comment.