fix: preserve original file type on upload — never convert PNG to JPG
CI/CD / CI · dotnet build (push) Successful in 53s
CI/CD / Deploy · drsousan (push) Successful in 28s

Problem: cropper always called out.toBlob(..., 'image/jpeg') regardless
of the original file type, silently converting PNGs to JPGs.

Fix:
- openCropper() now stores file.type and file.name on the cropper object
- applyCrop() uses the stored mime type for toBlob() and the filename
- Quality param only passed for lossy formats (jpeg/webp), not for PNG/GIF
- uploadImage() accept list expanded: svg, ico allowed
- Server-side: .svg and .ico added to allowed extensions

Result: PNG stays PNG, WebP stays WebP, ICO stays ICO.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-02 18:06:38 +03:30
parent e79ccf7e8c
commit 5d6a4a630d
2 changed files with 11 additions and 4 deletions
+10 -3
View File
@@ -1690,6 +1690,9 @@ const cropper = {
function openCropper(inputId, previewId, file) {
cropper.inputId = inputId; cropper.previewId = previewId;
// Preserve original file type so PNG stays PNG, WebP stays WebP, etc.
cropper.mimeType = file.type || 'image/jpeg';
cropper.fileName = file.name || 'upload';
const reader = new FileReader();
reader.onload = e => {
const img = new Image();
@@ -1831,7 +1834,8 @@ async function applyCrop() {
btn.disabled = true;
try {
const fd = new FormData();
fd.append('file', new File([blob], 'crop.jpg', {type:'image/jpeg'}));
const ext = _inputId && cropper.mimeType ? cropper.mimeType.split('/')[1].replace('jpeg','jpg') : 'jpg';
fd.append('file', new File([blob], `crop.${ext}`, {type: cropper.mimeType || 'image/jpeg'}));
const r = await fetch('/api/upload', {method:'POST', headers:{'Authorization':`Bearer ${token}`}, body:fd});
if (!r.ok) throw new Error(await r.text());
const { url } = await r.json();
@@ -1840,13 +1844,16 @@ async function applyCrop() {
toast('تصویر با موفقیت آپلود شد ✓');
} catch(e) { toast('خطا در آپلود: '+e.message,'error'); }
finally { btn.innerHTML=orig; btn.disabled=false; }
}, 'image/jpeg', 0.92);
// Use original mime type; only pass quality for lossy formats
const mime = cropper.mimeType || 'image/jpeg';
const quality = (mime === 'image/jpeg' || mime === 'image/webp') ? 0.92 : undefined;
}, mime, quality);
}
async function uploadImage(inputId, previewId) {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/jpeg,image/png,image/webp,image/gif';
fileInput.accept = 'image/jpeg,image/png,image/webp,image/gif,image/svg+xml,image/x-icon,image/vnd.microsoft.icon';
fileInput.onchange = () => {
const file = fileInput.files[0];
if (!file) return;