Skip to content

Commit

Permalink
properly encode file names for csv download
Browse files Browse the repository at this point in the history
fix
  • Loading branch information
Pascal-Delange committed Jan 3, 2025
1 parent cd1f3f3 commit 5e03365
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 5 deletions.
20 changes: 18 additions & 2 deletions packages/app-builder/src/routes/_builder+/lists+/$listId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,7 @@ function ClientDownloadAsCSV({ listId }: { listId: string }) {
}
const blob = await response.blob();
const contentDisposition = response.headers.get('Content-Disposition');
const filename =
contentDisposition?.split('filename=')[1] ?? 'list-values.csv';
const filename = parseContentDisposition(contentDisposition);

const url = URL.createObjectURL(blob);
await downloadFile(url, filename);
Expand All @@ -270,6 +269,23 @@ function ClientDownloadAsCSV({ listId }: { listId: string }) {
);
}

function parseContentDisposition(header: string | null) {
if (!header) {
return 'list-values.csv';
}
const filenameMatch = header.match(/filename\*=UTF-8''([^;]+)/i);
if (filenameMatch && filenameMatch[1]) {
return decodeURIComponent(filenameMatch[1]);
}
// Fallback to simple filename
const simpleMatch = header.match(/filename="?([^";\n]+)"?/i);
if (simpleMatch && simpleMatch[1]) {
return simpleMatch[1];
}

return 'list-values.csv';
}

function DownloadAsCSV({ listId }: { listId: string }) {
return (
<ClientOnly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function useDownloadCaseFiles(
);
}
const { url } = fileDownloadUrlSchema.parse(await response.json());
await downloadFile(url);
await downloadFile(url, 'download');
} catch (error) {
if (
error instanceof AlreadyDownloadingError ||
Expand Down
14 changes: 12 additions & 2 deletions packages/app-builder/src/utils/download-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@ const TIME_TO_OPEN_DOWNLOAD_MODALE = 150;
/**
* Utility function to download a file in the browser
*/
export async function downloadFile(url: string, filename?: string) {
export async function downloadFile(url: string, filename: string) {
return new Promise<void>((resolve, reject) => {
try {
const a = document.createElement('a');
a.href = url;
a.download = filename || 'download';
a.download = filename
.replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/\*/g, '%2A')
// These characters can be decoded safely
.replace(/%20/g, ' ')
.replace(/%2C/g, ',')
.replace(/%7C/g, '|')
.replace(/%60/g, '`')
.replace(/%5E/g, '^');

// Click handler that releases the object URL after the element has been clicked
// This is required for one-off downloads of the blob content
Expand Down

0 comments on commit 5e03365

Please sign in to comment.