feat: enhance file attachment features performance
This commit is contained in:
@@ -19,6 +19,32 @@ type FileStore = FileState & FileActions;
|
||||
|
||||
const MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
const guessMimeTypeFromName = (filename: string): string => {
|
||||
const name = (filename || "").toLowerCase();
|
||||
const ext = name.includes(".") ? name.split(".").pop() || "" : "";
|
||||
switch (ext) {
|
||||
case "png":
|
||||
return "image/png";
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
return "image/jpeg";
|
||||
case "gif":
|
||||
return "image/gif";
|
||||
case "webp":
|
||||
return "image/webp";
|
||||
case "svg":
|
||||
return "image/svg+xml";
|
||||
case "bmp":
|
||||
return "image/bmp";
|
||||
case "ico":
|
||||
return "image/x-icon";
|
||||
case "pdf":
|
||||
return "application/pdf";
|
||||
default:
|
||||
return "text/plain";
|
||||
}
|
||||
};
|
||||
|
||||
const guessMimeType = (file: File): string => {
|
||||
if (file.type && file.type.trim().length > 0) {
|
||||
return file.type;
|
||||
@@ -65,6 +91,31 @@ const guessMimeType = (file: File): string => {
|
||||
}
|
||||
};
|
||||
|
||||
const base64ByteLength = (base64: string): number => {
|
||||
const cleaned = base64.replace(/\s+/g, "");
|
||||
if (!cleaned) {
|
||||
return 0;
|
||||
}
|
||||
const padding = cleaned.endsWith("==") ? 2 : cleaned.endsWith("=") ? 1 : 0;
|
||||
return Math.floor((cleaned.length * 3) / 4) - padding;
|
||||
};
|
||||
|
||||
const base64EncodeBytes = (bytes: Uint8Array): string => {
|
||||
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
let output = "";
|
||||
for (let i = 0; i < bytes.length; i += 3) {
|
||||
const a = bytes[i] ?? 0;
|
||||
const b = bytes[i + 1];
|
||||
const c = bytes[i + 2];
|
||||
const triple = (a << 16) | ((b ?? 0) << 8) | (c ?? 0);
|
||||
output += alphabet[(triple >> 18) & 63];
|
||||
output += alphabet[(triple >> 12) & 63];
|
||||
output += typeof b === "number" ? alphabet[(triple >> 6) & 63] : "=";
|
||||
output += typeof c === "number" ? alphabet[triple & 63] : "=";
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
export const useFileStore = create<FileStore>()(
|
||||
|
||||
devtools(
|
||||
@@ -153,6 +204,8 @@ export const useFileStore = create<FileStore>()(
|
||||
}
|
||||
|
||||
let fileContent = content;
|
||||
let encoding: "base64" | undefined;
|
||||
let resolvedMimeType: string | undefined;
|
||||
if (!fileContent) {
|
||||
try {
|
||||
|
||||
@@ -171,6 +224,8 @@ export const useFileStore = create<FileStore>()(
|
||||
|
||||
if (response.data && "content" in response.data) {
|
||||
fileContent = response.data.content;
|
||||
encoding = response.data.encoding ?? undefined;
|
||||
resolvedMimeType = response.data.mimeType ?? undefined;
|
||||
} else {
|
||||
fileContent = "";
|
||||
}
|
||||
@@ -181,26 +236,36 @@ export const useFileStore = create<FileStore>()(
|
||||
}
|
||||
}
|
||||
|
||||
const blob = new Blob([fileContent || ""], { type: "text/plain" });
|
||||
const inferredMime = resolvedMimeType || guessMimeTypeFromName(name);
|
||||
const safeMimeType = inferredMime && inferredMime.trim().length > 0 ? inferredMime : "application/octet-stream";
|
||||
|
||||
if (blob.size > MAX_ATTACHMENT_SIZE) {
|
||||
const base64 = (() => {
|
||||
if (encoding === "base64") {
|
||||
return fileContent || "";
|
||||
}
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(fileContent || "");
|
||||
return base64EncodeBytes(data);
|
||||
})();
|
||||
|
||||
const sizeBytes = encoding === "base64"
|
||||
? base64ByteLength(base64)
|
||||
: new TextEncoder().encode(fileContent || "").length;
|
||||
|
||||
if (sizeBytes > MAX_ATTACHMENT_SIZE) {
|
||||
throw new Error(`File "${name}" is too large. Maximum size is 10MB.`);
|
||||
}
|
||||
|
||||
const file = new File([blob], name, { type: "text/plain" });
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(fileContent || "");
|
||||
const base64 = btoa(String.fromCharCode(...data));
|
||||
const dataUrl = `data:text/plain;base64,${base64}`;
|
||||
const file = new File([], name, { type: safeMimeType });
|
||||
const dataUrl = `data:${safeMimeType};base64,${base64}`;
|
||||
|
||||
const attachedFile: AttachedFile = {
|
||||
id: `server-file-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
|
||||
file,
|
||||
dataUrl,
|
||||
mimeType: "text/plain",
|
||||
mimeType: safeMimeType,
|
||||
filename: name,
|
||||
size: blob.size,
|
||||
size: sizeBytes,
|
||||
source: "server",
|
||||
serverPath: path,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user