From 43b2527275f9496f945fe82be06474979fc52359 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 9 Apr 2025 09:03:38 -0700 Subject: [PATCH] Fix: correct download filename in 2.15.0 (#9599) --------- Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com> --- .../document-detail.component.ts | 80 ++++++++++--------- src/documents/views.py | 10 ++- src/paperless/settings.py | 4 + 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 130acbd05..d9d78206f 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -1,5 +1,5 @@ import { AsyncPipe, NgTemplateOutlet } from '@angular/common' -import { HttpClient } from '@angular/common/http' +import { HttpClient, HttpResponse } from '@angular/common/http' import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' import { FormArray, @@ -995,44 +995,48 @@ export class DocumentDetailComponent this.documentId, original ) - this.http.get(downloadUrl, { responseType: 'blob' }).subscribe({ - next: (blob) => { - this.downloading = false - const blobParts = [blob] - const file = new File( - blobParts, - original - ? this.document.original_file_name - : this.document.archived_file_name, - { - type: original ? this.document.mime_type : 'application/pdf', - } - ) - if ( - !this.deviceDetectorService.isDesktop() && - navigator.canShare && - navigator.canShare({ files: [file] }) - ) { - navigator.share({ - files: [file], + this.http + .get(downloadUrl, { observe: 'response', responseType: 'blob' }) + .subscribe({ + next: (response: HttpResponse) => { + const filename = response.headers + .get('Content-Disposition') + ?.split(';') + ?.find((part) => part.trim().startsWith('filename=')) + ?.split('=')[1] + ?.replace(/['"]/g, '') + const blob = new Blob([response.body], { + type: response.body.type, }) - } else { - const url = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = this.document.title - a.click() - URL.revokeObjectURL(url) - } - }, - error: (error) => { - this.downloading = false - this.toastService.showError( - $localize`Error downloading document`, - error - ) - }, - }) + this.downloading = false + const file = new File([blob], filename, { + type: response.body.type, + }) + if ( + !this.deviceDetectorService.isDesktop() && + navigator.canShare && + navigator.canShare({ files: [file] }) + ) { + navigator.share({ + files: [file], + }) + } else { + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = filename + a.click() + URL.revokeObjectURL(url) + } + }, + error: (error) => { + this.downloading = false + this.toastService.showError( + $localize`Error downloading document`, + error + ) + }, + }) } hasNext() { diff --git a/src/documents/views.py b/src/documents/views.py index 7298391f2..27f8ed51f 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -2376,9 +2376,13 @@ def serve_file(*, doc: Document, use_archive: bool, disposition: str): # RFC 5987 addresses this issue # see https://datatracker.ietf.org/doc/html/rfc5987#section-4.2 # Chromium cannot handle commas in the filename - filename_normalized = normalize("NFKD", filename.replace(",", "_")).encode( - "ascii", - "ignore", + filename_normalized = ( + normalize("NFKD", filename.replace(",", "_")) + .encode( + "ascii", + "ignore", + ) + .decode("ascii") ) filename_encoded = quote(filename) content_disposition = ( diff --git a/src/paperless/settings.py b/src/paperless/settings.py index b161d7016..361854a93 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -565,6 +565,10 @@ if DEBUG: # Allow access from the angular development server during debugging CORS_ALLOWED_ORIGINS.append("http://localhost:4200") +CORS_EXPOSE_HEADERS = [ + "Content-Disposition", +] + ALLOWED_HOSTS = __get_list("PAPERLESS_ALLOWED_HOSTS", ["*"]) if ALLOWED_HOSTS != ["*"]: # always allow localhost. Necessary e.g. for healthcheck in docker.