feat(upload): 实现Canvas转换法,彻底解决远端服务器图片上传限制

This commit is contained in:
hangyu.tao 2026-02-02 11:39:14 +08:00
parent 7964ccd062
commit db4ce203b9
2 changed files with 43 additions and 44 deletions

View File

@ -1180,7 +1180,7 @@
</div>
<!-- Scripts -->
<script src="/static/js/main.js?v=5.0"></script>
<script src="/static/js/main.js?v=5.1"></script>
<script src="/static/js/trial-periods.js?v=1.3"></script>
<script src="/static/js/trial-periods-page.js?v=1.6"></script>
</body>

View File

@ -61,7 +61,7 @@ function canDelete() {
}
document.addEventListener("DOMContentLoaded", function () {
console.log("CRM System Main JS v5.0 Loaded - " + new Date().toLocaleString());
console.log("CRM System Main JS v5.1 Loaded - " + new Date().toLocaleString());
// 登录守卫
const token = localStorage.getItem("crmToken");
if (!token && !window.location.pathname.endsWith("login.html")) {
@ -3235,25 +3235,42 @@ document.addEventListener("DOMContentLoaded", function () {
const newBase64Images = [];
// Helper to convert file to base64 with retry
const fileToBase64 = async (f, attempt = 1) => {
// Canvas-based conversion - most compatible method for restricted environments
const fileToBase64Canvas = (file) => {
return new Promise((resolve, reject) => {
// Create object URL from file
const url = URL.createObjectURL(file);
const img = new Image();
img.onload = () => {
try {
return await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error || new Error("FileReader error"));
reader.onabort = () => reject(new Error("File read aborted"));
reader.readAsDataURL(f);
// Create canvas and draw image
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
// Convert to base64
const base64 = canvas.toDataURL(file.type || 'image/png');
// Clean up
URL.revokeObjectURL(url);
resolve(base64);
} catch (error) {
URL.revokeObjectURL(url);
reject(error);
}
};
img.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error('Failed to load image'));
};
img.src = url;
});
} catch (err) {
// If NotReadableError, retry up to 3 times
if (attempt < 3 && (err.name === "NotReadableError" || err.code === 1)) {
console.warn(`Retrying ${f.name} read due to error (attempt ${attempt})...`);
await new Promise(r => setTimeout(r, 200 * attempt));
return fileToBase64(f, attempt + 1);
}
throw err;
}
};
for (const file of files) {
@ -3263,31 +3280,13 @@ document.addEventListener("DOMContentLoaded", function () {
}
try {
console.log(`[v5.0] Converting ${file.name} (${file.size} bytes)...`);
const base64 = await fileToBase64(file);
console.log(`[v5.1-Canvas] Converting ${file.name} (${file.size} bytes)...`);
const base64 = await fileToBase64Canvas(file);
newBase64Images.push(base64);
console.log(`Successfully converted ${file.name}`);
console.log(`Successfully converted ${file.name} using Canvas method`);
} catch (error) {
console.error(`Primary method failed for ${file.name}:`, error);
// Fallback: arrayBuffer (usually works when FileReader fails)
try {
console.log(`Trying arrayBuffer fallback for ${file.name}...`);
const buffer = await file.arrayBuffer();
const bytes = new Uint8Array(buffer);
let binary = "";
const chunkSize = 8192;
for (let i = 0; i < bytes.length; i += chunkSize) {
const chunk = bytes.slice(i, i + chunkSize);
binary += String.fromCharCode.apply(null, chunk);
}
const b64 = `data:${file.type};base64,${btoa(binary)}`;
newBase64Images.push(b64);
console.log(`Fallback successful for ${file.name}`);
} catch (fallbackError) {
console.error(`Both methods failed for ${file.name}:`, fallbackError);
alert(`无法读取文件 "${file.name}"。\n这种情况通常是浏览器或系统文件锁定导致的。\n\n建议尝试:\n1. 刷新页面后再试\n2. 使用浏览器的“无痕模式”\n3. 确认文件没有被其他软件打开`);
}
console.error(`❌ Canvas method failed for ${file.name}:`, error);
alert(`无法处理文件 "${file.name}"。\n\n可能的原因:\n1. 图片格式不受支持\n2. 图片文件损坏\n3. 浏览器内存不足\n\n建议:\n• 尝试使用较小的图片\n• 转换为标准格式JPG/PNG\n• 刷新页面后重试`);
}
}