1

I want to deliver attachments from a third party system. They're stored as .attach files in the filsystem, original files are present in his database. Following this approach, my action look like this:

    public async Task<IActionResult> GetAttachment(int id) {
        var attachment = await myAttachmentManager.GetAttachmentAsync(id);
        // attachmentsPath = T:\attachments, FilePath = 1234.attach
        string fullPath = Path.Combine(attachmentsPath, attachment.FilePath); 
        var content = await File.ReadAllBytesAsync(fullPath);

        var fileContentProvider = new FileExtensionContentTypeProvider();
        string mimeType;
        if (!fileContentProvider.TryGetContentType(attachment.FileName, out mimeType)) // FileName = my-file.pdf
            mimeType = "application/octet-stream";
        var file = new FileContentResult(content, mimeType);

        // This should display "my-file.pdf" as title
        var contentHeader = new ContentDispositionHeaderValue("inline") {
            FileName = attachment.FileName
        }.ToString();
        Response.Headers["Content-Disposition"] = contentHeader;

        return file;
    }

This works, pdf files are embedded in browser and binary files like zip got downloaded. But when viewing pdfs, the browser show me 1234.attach as title in those example. I want to display the real file name (my-pdf.pdf here) and found Content-Disposition header for this.

Altough, Firefox and Chrome still display the id instead of my-pdf.pdf. When inspecting the page, it shows those title tag:

<title>1234</title>

What am I doing wrong?

Avoid setting the filename as id

Found this question where it seems that the id get parameter is used as filename. In my case its a real numeric id (1234) and I need to keep it since the files got verified by the database which use those id.

Lion
  • 16,606
  • 23
  • 86
  • 148

2 Answers2

1

You should set the FileDownloadName property of FileContentResult

byte[] content = your_byte[];

FileContentResult result = new FileContentResult(content, "application/octet-stream") 
                 {
                     FileDownloadName = String.Format("{0}.csv", fileName)
                 };

return result;

or

var file = new FileContentResult(content, mimeType);
file.FileDownloadName = String.Format("{0}.csv", fileName);
Saeed Rahimi
  • 235
  • 2
  • 8
  • Already tried that: When `FileDownloadName` is set, no embedded view of PDFs is possible. Instead they got downloaded. Indeed with corrent file name, but I want to embedd files like pdf or images when possible to increase usability. WIthout `FileDownloadName` the embedding at least works, altough it shows a non speaking filename. – Lion Dec 11 '18 at 11:40
  • 1
    look at this [answer](https://stackoverflow.com/a/40488246/2291208), it might help – Saeed Rahimi Dec 11 '18 at 12:03
  • I'm not using `HttpResponseMessage` but I tried `PhysicalFileResult`. Since the class shortens my code a bit it's usefull, but it's behavior is the same: As `FileDownloadName` property is set, the pdf got downloaded with it's correct name. Without that property the file is embedded, but has the `1234` id as tab title in the browser – Lion Dec 11 '18 at 18:32
1

It seems that Chrome pdf Viewer will read the pdf file and use the Title of the pdf document as the title of your page .

Reproduce

For a testing purpose, let's firstly download the the MQTT protocol standard . The Title property resides in the the 7184th Line

<</Title(MQTT Version 3.1.1) /Author(OASIS Message Queuing Telemetry Transport \(MQTT\) TC) /Creator .... >>

The line indicates the title of this pdf document is : MQTT Version 3.1.1.

Now let's test this pdf file with Chrome. Send a HTTP Request to /home/getattachment/123.pdf:

GET /home/getattachment/123.pdf HTTP/1.1
Host: localhost:5001
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7

and HTTP Response from the server is :

HTTP/1.1 200 OK
Content-Type: application/pdf
Server: Kestrel
Transfer-Encoding: chunked
Content-Disposition: inline; filename=wont_work_mqtt-v3.1.1-os.pdf; filename*=UTF-8''wont_work_mqtt-v3.1.1-os.pdf

Note the last segment of url is 123.pdf, and we specify the filename as wont_work_mqtt-v3.1.1-os.pdf in the Content-Disposition

and the screenshot below shows that the title of Tab and the title of the pdf view both are the original Title of the pdf document :

enter image description here

Walkaround

If I change the the Title property of the pdf document to (itminus_MQTT Version 3.1.1) as below :

<<
/Title (itminus_MQTT Version 3.1.1)
/CreationDate (D:20141105132636-05'00')
/ModDate (D:20141105132636-05'00')
/Author (OASIS Message Queuing Telemetry Transport \(MQTT\) TC)
/Producer (Select.Pdf for .NET v2018.4.0)
/Creator (Microsoft� Word 2010)
>>

the response will be :

enter image description here

So a walkaround is to modify the Title property of pdf document. You might choose your favorite library to do that as you like. Here I use the SelectPdf for a testing purpose :

var cdhv= new ContentDispositionHeaderValue("inline");
cdhv.SetHttpFileName( attachmentFileName);
Response.Headers["Content-Disposition"] = cdhv.ToString();
if(mimeType == "application/pdf"){
    SelectPdf.PdfDocument doc = null;
    try{
        doc = new SelectPdf.PdfDocument(fullPath);
        var docinfo = doc.DocumentInformation;
        docinfo.Title = attachmentFileName;
        doc.Save(Response.Body);
    }finally{
        doc?.Close();
    }
}else{
    Response.Body.Write(content);
}
return ;

It works for me.

itminus
  • 23,772
  • 2
  • 53
  • 88