feat: 上传图片时增加压缩逻辑

release/desktop
moonrailgun 3 years ago
parent 609b2fc6ab
commit c94932a85f

@ -159,6 +159,7 @@ importers:
axios: ^0.21.1
bundle-stats-webpack-plugin: ^3.2.9
clsx: ^1.1.1
compressorjs: ^1.1.1
copy-to-clipboard: ^3.3.1
copy-webpack-plugin: ^9.0.1
cross-env: ^7.0.3
@ -226,6 +227,7 @@ importers:
antd: 4.18.2_react-dom@17.0.2+react@17.0.2
axios: 0.21.4
clsx: registry.nlark.com/clsx/1.1.1
compressorjs: 1.1.1
copy-to-clipboard: 3.3.1
emoji-mart: 3.0.1_react@17.0.2
is-hotkey: 0.2.0
@ -3467,6 +3469,10 @@ packages:
resolution: {integrity: sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/big.js/download/big.js-5.2.2.tgz}
dev: true
/blueimp-canvas-to-blob/3.29.0:
resolution: {integrity: sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==}
dev: false
/body-parser/1.19.1:
resolution: {integrity: sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/body-parser/download/body-parser-1.19.1.tgz}
engines: {node: '>= 0.8'}
@ -3831,6 +3837,13 @@ packages:
dot-prop: registry.nlark.com/dot-prop/5.3.0
dev: true
/compressorjs/1.1.1:
resolution: {integrity: sha512-SysRuUPfmUNoq+RviE0iMFVUmoX2q/x+7PkEPUmk6NGkd85hDrmvujx0Qtp8UCGA6KMe5kuodsylPQcNaLf60w==}
dependencies:
blueimp-canvas-to-blob: 3.29.0
is-blob: 2.1.0
dev: false
/concat-map/0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@ -5320,6 +5333,11 @@ packages:
is-decimal: 1.0.4
dev: false
/is-blob/2.1.0:
resolution: {integrity: sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==}
engines: {node: '>=6'}
dev: false
/is-buffer/2.0.5:
resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==}
engines: {node: '>=4'}

@ -30,11 +30,13 @@
"k3172297b": "This feature is not yet open",
"k31a9d6a3": "The connect to the server is broken",
"k323b5cc7": "Recall",
"k3279c602": "Add now",
"k34b5e3ab": "Send Message",
"k34e357ee": "Group Summary",
"k35abe359": "Lobby",
"k35f486ba": "Nickname",
"k35f990b0": "View Detail",
"k393892b6": "Upload original image",
"k3ac17670": "An exception occurred, store create failed",
"k3b4b656d": "About",
"k3bbf3bbd": "Register Account",
@ -46,6 +48,7 @@
"k3f3597fc": "All",
"k3fe97dcc": "System settings",
"k41064134": "DM",
"k416e301a": "An exception occurred, Socket creation failed",
"k419da0ef": "Message explanation",
"k4231ab36": "Performance statistics",
"k424be044": "This invite expired in <2>{{date}}</2>",
@ -63,11 +66,13 @@
"k517db7e5": "Text Channel",
"k51db56bf": "Temporary Meeting",
"k551b0348": "Password",
"k56f9469b": "No friends yet",
"k57ab4d97": "Please select user",
"k58a85592": "Is not a valid plugin configuration",
"k5bb71ad7": "Installed",
"k5bec387": "Unable to get group information",
"k5f91e72c": "Built Plugins",
"k5fc9ccb6": "Operation too frequently",
"k61313787": "Login Tailchat",
"k61a1db2": "Already applied",
"k62051fcc": "Upload failed",
@ -134,6 +139,7 @@
"ka7907771": "Save Successful",
"ka7ecc377": "Unpin",
"kaa040a8e": "Default Group",
"kaaf56f78": "Debug",
"kabb39529": "Save changes",
"kabfe9512": "Save",
"kad207008": "Edit",

@ -30,11 +30,13 @@
"k3172297b": "该功能暂未开放",
"k31a9d6a3": "与服务器的链接已断开",
"k323b5cc7": "撤回",
"k3279c602": "立即添加",
"k34b5e3ab": "发送消息",
"k34e357ee": "群组概述",
"k35abe359": "大厅",
"k35f486ba": "用户昵称",
"k35f990b0": "查看详情",
"k393892b6": "上传原图",
"k3ac17670": "出现异常, Store 创建失败",
"k3b4b656d": "关于",
"k3bbf3bbd": "注册账号",
@ -46,6 +48,7 @@
"k3f3597fc": "全员",
"k3fe97dcc": "系统设置",
"k41064134": "私信",
"k416e301a": "出现异常, Socket 创建失败",
"k419da0ef": "消息解释",
"k4231ab36": "性能统计",
"k424be044": "该邀请将于 <2>{{date}}</2> 过期",
@ -63,11 +66,13 @@
"k517db7e5": "文字频道",
"k51db56bf": "临时会议",
"k551b0348": "密码",
"k56f9469b": "暂无好友",
"k57ab4d97": "请选择用户",
"k58a85592": "不是一个合法的插件配置",
"k5bb71ad7": "已安装",
"k5bec387": "无法获取到群组信息",
"k5f91e72c": "内置插件",
"k5fc9ccb6": "操作过于频繁",
"k61313787": "登录 Tailchat",
"k61a1db2": "已申请",
"k62051fcc": "上传失败",
@ -134,6 +139,7 @@
"ka7907771": "保存成功",
"ka7ecc377": "Unpin",
"kaa040a8e": "默认群组",
"kaaf56f78": "调试",
"kabb39529": "保存变更",
"kabfe9512": "保存",
"kad207008": "编辑",

@ -30,6 +30,7 @@
"antd": "^4.18.2",
"axios": "^0.21.1",
"clsx": "^1.1.1",
"compressorjs": "^1.1.1",
"copy-to-clipboard": "^3.3.1",
"emoji-mart": "^3.0.1",
"is-hotkey": "^0.2.0",

@ -1,6 +1,6 @@
import { closeModal, openModal } from '@/components/Modal';
import { ImageUploadPreviewer } from '@/components/modals/ImageUploadPreviewer';
import { fileToDataUrl } from '@/utils/file-helper';
import { compressImage, fileToDataUrl } from '@/utils/file-helper';
import React from 'react';
import { uploadFile } from 'tailchat-shared';
@ -20,14 +20,28 @@ export function uploadMessageImage(image: File): Promise<{
onCancel={() => {
closeModal(key);
}}
onConfirm={async (size) => {
const fileInfo = await uploadFile(image);
onConfirm={async (info) => {
let uploadImage = image;
const uploadOriginImage = info.uploadOriginImage;
if (uploadOriginImage === false) {
// 不上传原图
const originImageSize = image.size;
uploadImage = await compressImage(image);
const compressedImageSize = uploadImage.size;
console.log(
`压缩结果: ${
(compressedImageSize / originImageSize) * 100
}%(${originImageSize} -> ${compressedImageSize})`
);
}
const fileInfo = await uploadFile(uploadImage);
const imageRemoteUrl = fileInfo.url;
resolve({
url: imageRemoteUrl,
width: size.width,
height: size.height,
width: info.size.width,
height: info.size.height,
});
closeModal(key);
}}

@ -1,9 +1,9 @@
import { ModalWrapper } from '@/plugin/common';
import { Button } from '@/plugin/component';
import React, { useCallback, useRef } from 'react';
import { t, useAsyncFn } from 'tailchat-shared';
import React, { useCallback, useRef, useState } from 'react';
import { showToasts, t, useAsyncFn } from 'tailchat-shared';
import { useGlobalKeyDown } from '../../hooks/useGlobalKeyDown';
import { isEnterHotkey, isEscHotkey } from '../../utils/hot-key';
import { Switch, Button } from 'antd';
import { ModalWrapper } from '@/components/Modal';
interface ImageSize {
width: number;
@ -12,19 +12,36 @@ interface ImageSize {
interface ImageUploadPreviewerProps {
imageUrl: string;
onConfirm: (imageSize: ImageSize) => Promise<void>;
onConfirm: (imageUploadInfo: {
size: ImageSize;
uploadOriginImage: boolean;
}) => Promise<void>;
onCancel: () => void;
}
export const ImageUploadPreviewer: React.FC<ImageUploadPreviewerProps> =
React.memo((props) => {
const { imageUrl } = props;
const imageSizeRef = useRef<ImageSize>({ width: 0, height: 0 });
const [uploadOriginImage, setUploadOriginImage] = useState(false);
const [{ loading }, handleConfirm] = useAsyncFn(async () => {
if (
imageSizeRef.current.width === 0 ||
imageSizeRef.current.height === 0
) {
showToasts(t('操作过于频繁'), 'warning');
return;
}
if (typeof props.onConfirm === 'function') {
await Promise.resolve(props.onConfirm(imageSizeRef.current));
await Promise.resolve(
props.onConfirm({
size: imageSizeRef.current,
uploadOriginImage,
})
);
}
}, [props.onConfirm]);
}, [props.onConfirm, uploadOriginImage]);
useGlobalKeyDown(
(e) => {
@ -72,14 +89,26 @@ export const ImageUploadPreviewer: React.FC<ImageUploadPreviewerProps> =
</div>
</div>
<Button
className="mt-4"
type="primary"
loading={loading}
onClick={handleConfirm}
>
{t('确认')}
</Button>
<div className="w-full">
<div className="text-left">
<Switch
checked={uploadOriginImage}
onChange={(checked) => setUploadOriginImage(checked)}
/>
<span>{t('上传原图')}</span>
</div>
<div className="text-right">
<Button
className="mt-4"
type="primary"
loading={loading}
onClick={handleConfirm}
>
{t('确认')}
</Button>
</div>
</div>
</div>
</div>
</ModalWrapper>

@ -139,3 +139,33 @@ export async function openFile(
export const isGIF = (file: File): boolean => {
return file.type === 'image/gif';
};
/**
*
* 0.6
* @link https://www.npmjs.com/package/compressorjs
*/
export async function compressImage(
image: File,
options?: {
quality?: number;
maxWidth?: number;
maxHeight?: number;
}
): Promise<File> {
const { default: Compressor } = await import('compressorjs');
return new Promise((resolve, reject) => {
new Compressor(image, {
quality: options?.quality ?? 0.6,
maxWidth: options?.maxWidth ?? 1920,
maxHeight: options?.maxHeight ?? 1080,
success(file) {
resolve(file as File);
},
error(err) {
reject(err);
},
});
});
}

Loading…
Cancel
Save