拙劣的实现文件分卷压缩传输

This commit is contained in:
shuaiplus committed 2025-05-30 18:34:17 +08:00
1 parent 8fd3731819
commit 608174cabb
9 files changed
+700 -143

No files matched your search

+129
View File
@@ -897,3 +897,132 @@ body {
border-radius: 4px;
}
/* File message styles */
/* 文件消息样式 */
.file-message {
max-width: 300px;
background: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 12px;
padding: 12px;
margin: 4px 0;
font-family: inherit;
}
.file-info {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.file-icon {
font-size: 24px;
margin-right: 10px;
flex-shrink: 0;
}
.file-details {
flex: 1;
min-width: 0;
}
.file-name {
font-weight: 500;
font-size: 14px;
color: #333;
margin-bottom: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-meta {
font-size: 12px;
color: #666;
display: flex;
gap: 8px;
}
.file-size {
font-weight: 500;
}
.file-volumes {
color: #888;
}
.file-progress-container {
margin-bottom: 8px;
}
.file-progress-bar {
width: 100%;
height: 6px;
background: #e0e0e0;
border-radius: 3px;
overflow: hidden;
margin-bottom: 4px;
}
.file-progress {
height: 100%;
background: linear-gradient(90deg, #30a8f7, #4fc3f7);
border-radius: 3px;
transition: width 0.3s ease;
width: 0%;
}
.file-status {
font-size: 11px;
color: #666;
text-align: center;
}
.file-download-btn {
width: 100%;
padding: 8px 12px;
background: #30a8f7;
color: white;
border: none;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s ease, transform 0.1s ease;
display: none;
}
.file-download-btn:hover {
background: #2196f3;
transform: translateY(-1px);
}
.file-download-btn:active {
transform: translateY(0);
}
.file-download-btn:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
}
/* File message in bubbles */
.bubble .file-message {
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.bubble.me .file-message {
background: rgba(255, 255, 255, 0.95);
}
.bubble.other .file-message {
background: #fff;
}
.bubble.private-message .file-message {
background: rgba(255, 248, 220, 0.9);
border-color: rgba(255, 193, 7, 0.3);
}
+2 -3
View File
@@ -332,11 +332,10 @@ class NodeCrypt {
}
}
return
}
if (!this.channel[serverDecrypted.c].username) {
} if (!this.channel[serverDecrypted.c].username) {
return
}
if (clientDecrypted.a === 'm' && this.isString(clientDecrypted.t) && this.isString(clientDecrypted.d)) {
if (clientDecrypted.a === 'm' && this.isString(clientDecrypted.t) && (this.isString(clientDecrypted.d) || this.isObject(clientDecrypted.d))) {
if (this.callbacks.onClientMessage) {
try {
this.callbacks.onClientMessage({
+138 -4
View File
@@ -19,6 +19,9 @@ import {
addClass,
removeClass
} from './util.dom.js';
import {
formatFileSize
} from './util.file.js';
// Render the chat area
// 渲染聊天区域
@@ -54,11 +57,13 @@ export function addMsg(text, isHistory = false, msgType = 'text', timestamp = nu
if (!chatArea) return;
const className = 'bubble me' + (msgType.includes('_private') ? ' private-message' : '');
const date = new Date(ts);
const time = date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0');
let contentHtml = '';
const time = date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0'); let contentHtml = '';
if (msgType === 'image' || msgType === 'image_private') {
const safeImgSrc = escapeHTML(text).replace(/javascript:/gi, '');
contentHtml = `<img src="${safeImgSrc}"alt="image"class="bubble-img">`
} else if (msgType === 'file' || msgType === 'file_private') {
// Handle file messages
contentHtml = renderFileMessage(text, true);
} else {
contentHtml = textToHTML(text)
}
@@ -95,11 +100,13 @@ export function addOtherMsg(msg, userName = '', avatar = '', isHistory = false,
if (!chatArea) return;
const bubbleWrap = createElement('div', {
class: 'bubble-other-wrap'
});
let contentHtml = '';
}); let contentHtml = '';
if (msgType === 'image' || msgType === 'image_private') {
const safeImgSrc = escapeHTML(msg).replace(/javascript:/gi, '');
contentHtml = `<img src="${safeImgSrc}"alt="image"class="bubble-img">`
} else if (msgType === 'file' || msgType === 'file_private') {
// Handle file messages
contentHtml = renderFileMessage(msg, false);
} else {
contentHtml = textToHTML(msg)
}
@@ -254,6 +261,133 @@ export function showImageModal(src) {
updateTransform()
}
// Render file message content
// 渲染文件消息内容
function renderFileMessage(fileData, isSender) {
const {
fileId,
fileName,
originalSize,
totalVolumes
} = fileData;
const safeFileName = escapeHTML(fileName);
const formattedSize = formatFileSize(originalSize);
// Determine file icon based on extension
// 根据扩展名确定文件图标
const fileExtension = fileName.split('.').pop().toLowerCase();
let fileIcon = '📄'; // Default document icon 默认文档图标
const iconMap = {
// Images
'jpg': '🖼️',
'jpeg': '🖼️',
'png': '🖼️',
'gif': '🖼️',
'bmp': '🖼️',
'svg': '🖼️',
'webp': '🖼️',
// Videos
'mp4': '🎬',
'avi': '🎬',
'mov': '🎬',
'mkv': '🎬',
'wmv': '🎬',
'flv': '🎬',
'webm': '🎬',
// Audio
'mp3': '🎵',
'wav': '🎵',
'flac': '🎵',
'aac': '🎵',
'ogg': '🎵',
'm4a': '🎵',
// Documents
'pdf': '📕',
'doc': '📄',
'docx': '📄',
'txt': '📄',
'rtf': '📄',
'xls': '📊',
'xlsx': '📊',
'csv': '📊',
'ppt': '📈',
'pptx': '📈',
// Archives
'zip': '🗜️',
'rar': '🗜️',
'7z': '🗜️',
'tar': '🗜️',
'gz': '🗜️',
// Code
'js': '💻',
'html': '💻',
'css': '💻',
'py': '💻',
'java': '💻',
'cpp': '💻',
'c': '💻',
'php': '💻',
'rb': '💻',
'go': '💻',
'ts': '💻',
'json': '💻',
'xml': '💻'
};
if (iconMap[fileExtension]) {
fileIcon = iconMap[fileExtension];
}
// Check actual file transfer status
const transfer = window.fileTransfers ? window.fileTransfers.get(fileId) : null;
let statusText = 'Waiting...';
let progressWidth = '0%';
let downloadBtnStyle = 'display: none;';
if (transfer) {
if (transfer.status === 'sending') {
const progress = (transfer.sentVolumes / transfer.totalVolumes) * 100;
progressWidth = `${progress}%`;
statusText = `Sending ${transfer.sentVolumes}/${transfer.totalVolumes}`;
} else if (transfer.status === 'receiving') {
const progress = (transfer.receivedVolumes.size / transfer.totalVolumes) * 100;
progressWidth = `${progress}%`;
statusText = `Receiving ${transfer.receivedVolumes.size}/${transfer.totalVolumes}`;
} else if (transfer.status === 'completed') {
progressWidth = '100%';
statusText = '✓ Completed';
downloadBtnStyle = isSender ? 'display: none;' : 'display: inline-block;';
}
} else if (isSender) {
statusText = 'Sent';
progressWidth = '100%';
}
return `
<div class="file-message" data-file-id="${fileId}">
<div class="file-info">
<div class="file-icon">${fileIcon}</div>
<div class="file-details">
<div class="file-name" title="${safeFileName}">${safeFileName}</div>
<div class="file-meta">
<span class="file-size">${formattedSize}</span>
<span class="file-volumes">${totalVolumes} volumes</span>
</div>
</div>
</div>
<div class="file-progress-container">
<div class="file-progress-bar">
<div class="file-progress" style="width: ${progressWidth}"></div>
</div>
<div class="file-status">${statusText}</div>
</div>
<button class="file-download-btn" style="${downloadBtnStyle}" onclick="window.downloadFile('${fileId}')">
📥 Download
</button>
</div>
`;
}
// Automatically adjust the height of the input area
// 自动调整输入区域的高度
export function autoGrowInput() {
+30 -19
View File
@@ -2,11 +2,13 @@
// Import the NodeCrypt module (used for encryption)
import './NodeCrypt.js';
// 从 util.image.js 中导入设置图片发送的函数
// Import setupImageSend function from util.image.js
// 从 util.file.js 中导入设置文件发送的函数
// Import setupFileSend function from util.file.js
import {
setupImageSend
} from './util.image.js';
setupFileSend,
handleFileMessage,
downloadFile
} from './util.file.js';
// 从 util.emoji.js 中导入设置表情选择器的函数
// Import setupEmojiPicker function from util.emoji.js
@@ -75,6 +77,8 @@ window.addSystemMsg = addSystemMsg;
window.joinRoom = joinRoom;
window.notifyMessage = notifyMessage;
window.setupEmojiPicker = setupEmojiPicker;
window.handleFileMessage = handleFileMessage;
window.downloadFile = downloadFile;
// 当 DOM 内容加载完成后执行初始化逻辑
// Run initialization logic when the DOM content is fully loaded
@@ -176,25 +180,24 @@ window.addEventListener('DOMContentLoaded', () => {
}
});
}
// 设置发送图片功能
// Setup image sending functionality
setupImageSend({
// 设置发送文件功能
// Setup file sending functionality
setupFileSend({
inputSelector: '.input-message-input', // 消息输入框选择器 / Message input selector
attachBtnSelector: '.chat-attach-btn', // 附件按钮选择器 / Attach button selector
fileInputSelector: '.new-message-wrapper input[type="file"]', // 文件输入框选择器 / File input selector
onSend: (dataUrl) => {
onSend: (message) => {
const rd = roomsData[activeRoomIndex];
if (rd && rd.chat) {
if (rd.privateChatTargetId) {
// 私聊图片加密并发送
// Encrypt and send private image
// 私聊文件加密并发送
// Encrypt and send private file message
const targetClient = rd.chat.channel[rd.privateChatTargetId];
if (targetClient && targetClient.shared) {
const clientMessagePayload = {
a: 'm',
t: 'image_private',
d: dataUrl
t: message.type + '_private',
d: message
};
const encryptedClientMessage = rd.chat.encryptClientMessage(clientMessagePayload, targetClient.shared);
const serverRelayPayload = {
@@ -204,15 +207,23 @@ window.addEventListener('DOMContentLoaded', () => {
};
const encryptedMessageForServer = rd.chat.encryptServerMessage(serverRelayPayload, rd.chat.serverShared);
rd.chat.sendMessage(encryptedMessageForServer);
addMsg(dataUrl, false, 'image_private');
// 添加到自己的聊天记录
if (message.type === 'file_start') {
addMsg(message, false, 'file_private');
}
} else {
addSystemMsg(`Cannot send private image to ${rd.privateChatTargetName}.User might not be fully connected.`)
addSystemMsg(`Cannot send private file to ${rd.privateChatTargetName}. User might not be fully connected.`)
}
} else {
// 公共频道发送图片
// Send image to public channel
rd.chat.sendChannelMessage('image', dataUrl);
addMsg(dataUrl, false, 'image');
// 公共频道文件发送
// Send file to public channel
rd.chat.sendChannelMessage(message.type, message);
// 添加到自己的聊天记录
if (message.type === 'file_start') {
addMsg(message, false, 'file');
}
}
}
}
+42
View File
@@ -242,10 +242,52 @@ export function handleClientMessage(idx, msg) {
return
}
}
let msgType = msg.type || 'text';
// Handle file messages
if (msgType.startsWith('file_')) {
// Import handleFileMessage function if needed
if (window.handleFileMessage) {
window.handleFileMessage(msg.data, msgType.includes('_private'));
}
// Only add file_start messages to chat history
if (msgType === 'file_start' || msgType === 'file_start_private') {
let realUserName = msg.userName;
if (!realUserName && msg.clientId && newRd.userMap[msg.clientId]) {
realUserName = newRd.userMap[msg.clientId].userName || newRd.userMap[msg.clientId].username || newRd.userMap[msg.clientId].name
}
roomsData[idx].messages.push({
type: 'other',
text: msg.data,
userName: realUserName,
avatar: realUserName,
msgType: msgType.includes('_private') ? 'file_private' : 'file',
timestamp: Date.now()
});
const notificationMsgType = msgType.includes('_private') ? 'private file' : 'file';
if (window.notifyMessage) {
window.notifyMessage(newRd.roomName, notificationMsgType, `${msg.data.fileName}`, realUserName)
}
if (activeRoomIndex !== idx) {
roomsData[idx].unreadCount = (roomsData[idx].unreadCount || 0) + 1;
renderRooms(activeRoomIndex)
} else {
renderChatArea()
}
}
return;
}
// Handle legacy image detection
if (!msgType.includes('_private') && msg.data && msg.data.startsWith('data:image/')) {
msgType = 'image'
}
let realUserName = msg.userName;
if (!realUserName && msg.clientId && newRd.userMap[msg.clientId]) {
realUserName = newRd.userMap[msg.clientId].userName || newRd.userMap[msg.clientId].username || newRd.userMap[msg.clientId].name
+351
View File
@@ -0,0 +1,351 @@
// Import necessary modules
// 导入必要的模块
import {
$,
$id,
createElement,
on,
addClass,
removeClass
} from './util.dom.js';
import { deflate } from 'fflate';
// File transfer state management
// 文件传输状态管理
window.fileTransfers = new Map();
// Generate unique file ID
// 生成唯一文件ID
function generateFileId() {
return 'file_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
// Compress file into volumes
// 将文件压缩为分卷
async function compressFileToVolumes(file, volumeSize = 128 * 1024) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(e) {
const arrayBuffer = new Uint8Array(e.target.result);
// Compress the file data
deflate(arrayBuffer, { level: 6 }, (err, compressed) => {
if (err) {
reject(err);
return;
}
// Split into volumes
const volumes = [];
for (let i = 0; i < compressed.length; i += volumeSize) {
const volume = compressed.slice(i, i + volumeSize);
// Convert to base64 without using spread operator to avoid stack overflow
let binaryString = '';
for (let j = 0; j < volume.length; j++) {
binaryString += String.fromCharCode(volume[j]);
}
volumes.push(btoa(binaryString));
}
resolve({
volumes,
originalSize: file.size,
compressedSize: compressed.length
});
});
};
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(file);
});
}
// Decompress volumes back to file
// 将分卷解压回文件
async function decompressVolumesToFile(volumes, fileName) { try {
// Combine all volumes
const combinedData = volumes.map(volume => {
const binaryString = atob(volume);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
});
const totalLength = combinedData.reduce((sum, arr) => sum + arr.length, 0);
const compressed = new Uint8Array(totalLength);
let offset = 0;
for (const data of combinedData) {
compressed.set(data, offset);
offset += data.length;
}
// Decompress
return new Promise((resolve, reject) => {
import('fflate').then(({ inflate }) => {
inflate(compressed, (err, decompressed) => {
if (err) {
reject(err);
return;
}
// Create blob and download
const blob = new Blob([decompressed]);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
resolve();
});
});
});
} catch (error) {
console.error('Decompression error:', error);
throw error;
}
}
// Setup file sending functionality
// 设置文件发送功能
export function setupFileSend({
inputSelector,
attachBtnSelector,
fileInputSelector,
onSend
}) {
const attachBtn = document.querySelector(attachBtnSelector);
const fileInput = document.querySelector(fileInputSelector);
if (attachBtn && fileInput) {
// 移除文件类型限制,接受所有文件
// Remove file type restriction, accept all files
fileInput.accept = '*/*';
// 点击附件按钮触发文件选择
// Click attach button to trigger file selection
attachBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
fileInput.click();
});
// 处理文件选择
// Handle file selection
fileInput.addEventListener('change', async (e) => {
const files = e.target.files;
if (files && files.length > 0) {
const file = files[0];
const fileId = generateFileId();
try {
// 显示压缩进度
window.addSystemMsg(`Compressing ${file.name}...`);
// 压缩文件为分卷
const { volumes, originalSize, compressedSize } = await compressFileToVolumes(file);
// 创建文件传输状态
const fileTransfer = {
fileId,
fileName: file.name,
originalSize,
compressedSize,
totalVolumes: volumes.length,
sentVolumes: 0,
status: 'sending'
};
window.fileTransfers.set(fileId, fileTransfer);
// 发送文件开始消息
onSend({
type: 'file_start',
fileId,
fileName: file.name,
originalSize,
compressedSize,
totalVolumes: volumes.length
});
// 逐个发送分卷
for (let i = 0; i < volumes.length; i++) {
onSend({
type: 'file_volume',
fileId,
volumeIndex: i,
volumeData: volumes[i],
isLast: i === volumes.length - 1
});
// 更新发送进度
fileTransfer.sentVolumes = i + 1;
updateFileProgress(fileId);
// 添加小延迟避免消息过快
await new Promise(resolve => setTimeout(resolve, 50));
}
// 发送完成消息
onSend({
type: 'file_complete',
fileId
});
fileTransfer.status = 'completed';
updateFileProgress(fileId);
} catch (error) {
console.error('File compression error:', error);
window.addSystemMsg(`Failed to compress ${file.name}: ${error.message}`);
}
}
// 清空文件输入
e.target.value = '';
});
}
}
// Update file progress in chat
// 更新聊天中的文件进度
function updateFileProgress(fileId) {
const transfer = window.fileTransfers.get(fileId);
if (!transfer) return;
const elements = document.querySelectorAll(`[data-file-id="${fileId}"]`);
elements.forEach(element => {
const progressBar = element.querySelector('.file-progress');
const statusText = element.querySelector('.file-status');
const downloadBtn = element.querySelector('.file-download-btn');
if (transfer.status === 'sending') {
const progress = (transfer.sentVolumes / transfer.totalVolumes) * 100;
if (progressBar) progressBar.style.width = `${progress}%`;
if (statusText) statusText.textContent = `Sending ${transfer.sentVolumes}/${transfer.totalVolumes}`;
} else if (transfer.status === 'receiving') {
const progress = (transfer.receivedVolumes.size / transfer.totalVolumes) * 100;
if (progressBar) progressBar.style.width = `${progress}%`;
if (statusText) statusText.textContent = `Receiving ${transfer.receivedVolumes.size}/${transfer.totalVolumes}`;
} else if (transfer.status === 'completed') {
if (progressBar) progressBar.style.width = '100%';
if (statusText) statusText.textContent = '✓ Completed';
if (downloadBtn) {
downloadBtn.style.display = 'inline-block';
downloadBtn.disabled = false;
}
}
});
}
// Handle incoming file messages
// 处理接收到的文件消息
export function handleFileMessage(message, isPrivate = false) {
const { type, fileId } = message;
switch (type) {
case 'file_start':
handleFileStart(message, isPrivate);
break;
case 'file_volume':
handleFileVolume(message);
break;
case 'file_complete':
handleFileComplete(message);
break;
}
}
// Handle file start message
// 处理文件开始消息
function handleFileStart(message, isPrivate) {
const { fileId, fileName, originalSize, compressedSize, totalVolumes } = message;
const fileTransfer = {
fileId,
fileName,
originalSize,
compressedSize,
totalVolumes,
receivedVolumes: new Set(),
volumeData: new Array(totalVolumes),
status: 'receiving'
};
window.fileTransfers.set(fileId, fileTransfer);
// 添加文件消息到聊天
if (window.addOtherMsg) {
window.addOtherMsg({
type: 'file',
fileId,
fileName,
originalSize,
totalVolumes
}, '', '', false, isPrivate ? 'file_private' : 'file');
}
}
// Handle file volume message
// 处理文件分卷消息
function handleFileVolume(message) {
const { fileId, volumeIndex, volumeData } = message;
const transfer = window.fileTransfers.get(fileId);
if (!transfer) return;
transfer.receivedVolumes.add(volumeIndex);
transfer.volumeData[volumeIndex] = volumeData;
updateFileProgress(fileId);
}
// Handle file complete message
// 处理文件完成消息
function handleFileComplete(message) {
const { fileId } = message;
const transfer = window.fileTransfers.get(fileId);
if (!transfer) return;
// 检查是否所有分卷都已接收
if (transfer.receivedVolumes.size === transfer.totalVolumes) {
transfer.status = 'completed';
updateFileProgress(fileId);
}
}
// Download file from volumes
// 从分卷下载文件
export async function downloadFile(fileId) {
const transfer = window.fileTransfers.get(fileId);
if (!transfer || transfer.status !== 'completed') return;
try {
await decompressVolumesToFile(transfer.volumeData, transfer.fileName);
window.addSystemMsg(`Downloaded ${transfer.fileName}`);
} catch (error) {
console.error('Download error:', error);
window.addSystemMsg(`Failed to download ${transfer.fileName}: ${error.message}`);
}
}
// Format file size
// 格式化文件大小
export function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Legacy image send function for backward compatibility
// 向后兼容的图片发送函数
export function setupImageSend(config) {
setupFileSend(config);
}
-117
View File
@@ -1,117 +0,0 @@
// Import DOM helpers
// 导入 DOM 辅助函数
import {
$,
on,
createElement
} from './util.dom.js';
// Process and compress image file
// 处理并压缩图片文件
export async function processImage(file, callback) {
// Create image element for loading file
// 创建图片元素用于加载文件
const img = new Image();
img.onload = function() {
const maxW = 1280, // Max width
maxH = 1280; // 最大宽度/高度
let w = img.naturalWidth,
h = img.naturalHeight;
// Resize if too large
// 如果图片过大则缩放
if (w > maxW || h > maxH) {
const scale = Math.min(maxW / w, maxH / h);
w = Math.round(w * scale);
h = Math.round(h * scale)
}
// Create canvas for drawing image
// 创建画布用于绘制图片
const canvas = createElement('canvas');
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, w, h);
let dataUrl;
// Export as webp
// 导出为 webp
dataUrl = canvas.toDataURL('image/webp', 0.80);
callback(dataUrl)
};
// Read file as data URL
// 以 dataURL 方式读取文件
const reader = new FileReader();
reader.onload = function(e) {
img.src = e.target.result
};
reader.readAsDataURL(file)
}
// Translate message key
// 翻译消息 key
function t(key) {
const messages = {
tooLarge: 'Image is too large (over 5MB)', // 图片过大(超过5MB)
};
return messages[key] || key
}
// Setup image send logic for UI
// 设置图片发送相关 UI 逻辑
export function setupImageSend({
inputSelector,
attachBtnSelector,
fileInputSelector,
onSend
}) {
const input = $(inputSelector);
const attachBtn = $(attachBtnSelector);
const fileInput = $(fileInputSelector);
if (fileInput) fileInput.setAttribute('accept', 'image/*');
if (attachBtn && fileInput) {
// Click attach triggers file input
// 点击附件按钮触发文件选择
on(attachBtn, 'click', () => fileInput.click());
// Handle file input change
// 处理文件选择变化
on(fileInput, 'change', async function() {
if (!fileInput.files || !fileInput.files.length) return;
const file = fileInput.files[0];
// Only allow image files
// 只允许图片文件
if (!file.type.startsWith('image/')) return;
// Check file size
// 检查文件大小
if (file.size > 5 * 1024 * 1024) {
alert(t('tooLarge'));
return
}
processImage(file, (dataUrl) => {
if (typeof onSend === 'function') onSend(dataUrl)
});
fileInput.value = ''
})
}
if (input) {
// Paste image from clipboard
// 从剪贴板粘贴图片
on(input, 'paste', function(e) {
if (!e.clipboardData) return;
for (const item of e.clipboardData.items) {
// Only handle image type
// 只处理图片类型
if (item.type.startsWith('image/')) {
const file = item.getAsFile();
if (!file) continue;
// Check file size
// 检查文件大小
if (file.size > 5 * 1024 * 1024) {
alert(t('tooLarge'));
continue
}
processImage(file, (dataUrl) => {
if (typeof onSend === 'function') onSend(dataUrl)
});
e.preventDefault();
break
}
}
})
}
}