Fix: preserve non-ASCII filenames in document downloads (#9702)

This commit is contained in:
shamoon
2025-04-19 15:10:34 -07:00
committed by GitHub
parent abf910fd93
commit f52ebc7bf0
3 changed files with 59 additions and 6 deletions

View File

@@ -0,0 +1,31 @@
import { getFilenameFromContentDisposition } from './http'
describe('getFilenameFromContentDisposition', () => {
it('should extract filename from Content-Disposition header with filename*', () => {
const header = "attachment; filename*=UTF-8''example%20file.txt"
expect(getFilenameFromContentDisposition(header)).toBe('example file.txt')
})
it('should extract filename from Content-Disposition header with filename=', () => {
const header = 'attachment; filename="example-file.txt"'
expect(getFilenameFromContentDisposition(header)).toBe('example-file.txt')
})
it('should prioritize filename* over filename if both are present', () => {
const header =
'attachment; filename="fallback.txt"; filename*=UTF-8\'\'preferred%20file.txt'
const result = getFilenameFromContentDisposition(header)
expect(result).toBe('preferred file.txt')
})
it('should gracefully fall back to null', () => {
// invalid UTF-8 sequence
expect(
getFilenameFromContentDisposition("attachment; filename*=UTF-8''%E0%A4%A")
).toBeNull()
// missing filename
expect(getFilenameFromContentDisposition('attachment;')).toBeNull()
// empty header
expect(getFilenameFromContentDisposition(null)).toBeNull()
})
})

View File

@@ -0,0 +1,23 @@
export function getFilenameFromContentDisposition(header: string): string {
if (!header) {
return null
}
// Try filename* (RFC 5987)
const filenameStar = header.match(/filename\*=(?:UTF-\d['']*)?([^;]+)/i)
if (filenameStar?.[1]) {
try {
return decodeURIComponent(filenameStar[1])
} catch (e) {
// Ignore decoding errors and fall through
}
}
// Fallback to filename=
const filenameMatch = header.match(/filename="?([^"]+)"?/)
if (filenameMatch?.[1]) {
return filenameMatch[1]
}
return null
}