Fix file downloads for Safari with proper filename handling
Implement browser-specific download methods to ensure files download with correct filenames across all browsers. Safari now uses window.location.href while other browsers use blob URLs. Add Content-Disposition header to proxy endpoint for proper filename preservation.
This commit is contained in:
parent
b7544d82f3
commit
4d3935e863
|
|
@ -199,33 +199,51 @@ const handlePreviewError = (event: any) => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Download file
|
// Download file (with special handling for Safari)
|
||||||
const downloadFile = async () => {
|
const downloadFile = async () => {
|
||||||
if (!props.file) return;
|
if (!props.file) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use proxy download endpoint for better mobile compatibility
|
|
||||||
const proxyUrl = `/api/files/proxy-download?fileName=${encodeURIComponent(props.file.name)}`;
|
|
||||||
|
|
||||||
// Create a link element
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = proxyUrl;
|
|
||||||
|
|
||||||
// Extract clean filename for download
|
// Extract clean filename for download
|
||||||
let filename = props.file.displayName;
|
let filename = props.file.displayName;
|
||||||
if (!filename.includes('.') && props.file.extension) {
|
if (!filename.includes('.') && props.file.extension) {
|
||||||
filename += '.' + props.file.extension;
|
filename += '.' + props.file.extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
link.download = filename;
|
// Check if Safari (iOS or desktop)
|
||||||
link.style.display = 'none';
|
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
|
|
||||||
// Clean up
|
if (isSafari) {
|
||||||
setTimeout(() => {
|
// For Safari, use location.href to force proper filename handling
|
||||||
document.body.removeChild(link);
|
const downloadUrl = `/api/files/proxy-download?fileName=${encodeURIComponent(props.file.name)}`;
|
||||||
}, 100);
|
window.location.href = downloadUrl;
|
||||||
|
} else {
|
||||||
|
// For other browsers, use blob approach
|
||||||
|
const response = await fetch(`/api/files/proxy-download?fileName=${encodeURIComponent(props.file.name)}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to download file');
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
// Create object URL from blob
|
||||||
|
const downloadUrl = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// Create a link element
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = downloadUrl;
|
||||||
|
link.download = filename;
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(downloadUrl);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to download file:', err);
|
console.error('Failed to download file:', err);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -499,37 +499,58 @@ const createNewFolder = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Download file (using proxy for Safari compatibility)
|
// Download file (with special handling for Safari)
|
||||||
const downloadFile = async (file: FileItem) => {
|
const downloadFile = async (file: FileItem) => {
|
||||||
downloadingFiles.value[file.name] = true;
|
downloadingFiles.value[file.name] = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use proxy download endpoint for better mobile compatibility
|
|
||||||
const proxyUrl = `/api/files/proxy-download?fileName=${encodeURIComponent(file.name)}`;
|
|
||||||
|
|
||||||
// Create a link element
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = proxyUrl;
|
|
||||||
|
|
||||||
// Extract clean filename for download
|
// Extract clean filename for download
|
||||||
let filename = file.displayName;
|
let filename = file.displayName;
|
||||||
if (!filename.includes('.') && file.extension) {
|
if (!filename.includes('.') && file.extension) {
|
||||||
filename += '.' + file.extension;
|
filename += '.' + file.extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
link.download = filename;
|
// Check if Safari (iOS or desktop)
|
||||||
link.style.display = 'none';
|
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
|
|
||||||
// Clean up
|
if (isSafari) {
|
||||||
setTimeout(() => {
|
// For Safari, open in new window to force proper filename handling
|
||||||
document.body.removeChild(link);
|
const downloadUrl = `/api/files/proxy-download?fileName=${encodeURIComponent(file.name)}`;
|
||||||
}, 100);
|
window.location.href = downloadUrl;
|
||||||
|
} else {
|
||||||
|
// For other browsers, use blob approach
|
||||||
|
const response = await fetch(`/api/files/proxy-download?fileName=${encodeURIComponent(file.name)}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to download file');
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
// Create object URL from blob
|
||||||
|
const objectUrl = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// Create a link element
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = objectUrl;
|
||||||
|
link.download = filename;
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(objectUrl);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error('Failed to download file');
|
toast.error('Failed to download file');
|
||||||
} finally {
|
} finally {
|
||||||
downloadingFiles.value[file.name] = false;
|
// Add delay for Safari to prevent immediate loading state removal
|
||||||
|
setTimeout(() => {
|
||||||
|
downloadingFiles.value[file.name] = false;
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ export default defineEventHandler(async (event) => {
|
||||||
|
|
||||||
// Set headers for download
|
// Set headers for download
|
||||||
setHeader(event, 'Content-Type', contentType);
|
setHeader(event, 'Content-Type', contentType);
|
||||||
setHeader(event, 'Content-Disposition', `attachment; filename="${cleanFileName}"`);
|
// Use both filename and filename* for better compatibility
|
||||||
|
setHeader(event, 'Content-Disposition', `attachment; filename="${cleanFileName}"; filename*=UTF-8''${encodeURIComponent(cleanFileName)}`);
|
||||||
// Content-Length header is set automatically by Nitro when returning a buffer
|
// Content-Length header is set automatically by Nitro when returning a buffer
|
||||||
|
|
||||||
// Return the file buffer
|
// Return the file buffer
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue