63

We have an application that gets some input parameters and sends them to the back end where they get processed. The processing result is a PDF file that we want to open in a new tab.

The code doing this looks similar to the below:

let response = await myService.getDocument(); let file = new Blob([response.data], {type: 'application/pdf'}); let fileURL = URL.createObjectURL(file); window.open(fileURL, '_blank'); 

Everything works fine but the URL in the browser shows some random generated string as below:

blob:http://localhost:3000/85cad96e-e44e-a1f9-db97a96ed3fe 

Obviously this does not look very good to the end user and we would prefer to display something which is meaningful to the user, say something like:

blob:ftp://localhost:3000/my_document_name_or_whatever 

9 Answers 9

59

Short answer, You can't.

This is an address that points to the browser's memory, where it has stored your blob, or a pointer to the original file in case of user uploaded file through the input type=file.

This is somehow by design. You can create multiple of these blobURLs from the same Blob. If they were to use a filename as URI, you couldn't.

Theoretically, it should be possible for you to dynamically create a page that would redirect to the BlobURI, and you could name this redirection page as you which. But this is just theory, I never tried to do it myself.

A rough proof of concept can be seen in this plunker, obviously, you'll need to generate blobRename.html dynamically, and change its name to the one you want, and also force it's content-header so that the browser thinks it's an html page if you want to get rid of the .html. Also note that it doesn't seem to work with pdf files, which need browser plugins to trigger in, but with some more work, it may be possible to hack something around.

But anyway, I would just let the random url, your users will get more and more used to it as more and more web apps do use this great API.

Sign up to request clarification or add additional context in comments.

3 Comments

You can set file name, add below ** downloadLink.href = ${filename};**
Works in Firefox, doesn't work in Chrome sadly.
is there a way to change the name when we download this blob. ??? the blob is getting downloaded with the random uuid , that is part of the url i want to chang the name to a custom name . rather than the uuid when downloaded.
32

You can create a link with a download filename specified, then trigger a click to open it:

let file = new Blob([response.data], {type: 'application/pdf'}); let fileURL = URL.createObjectURL(file); // create <a> element dynamically let fileLink = document.createElement('a'); fileLink.href = fileURL; // suggest a name for the downloaded file fileLink.download = 'pdf_name'; // simulate click fileLink.click(); 

3 Comments

It works nicely if you want to download file, but seems doesn't help if you need to open it on the new tab.
And how to open in new tab instead of downloading?
That will download the file, it won't open in a new tab.
5

Answer for 2023

This is a frustrating problem for developers who want to generate a PDF inside the browser with something like pdf-lib while retaining control over the default filename offered to the user when they click the download/save button on the browser's built-in PDF viewer.

Back story

I wasted a bunch of time trying to get PDF.js to work in Svelte so it looks like the the default PDF viewer with just the download button overridden. I had one problem after the other. Everything seemed stacked against me and there were old Stack Overflow answers and blog posts casually mentioning their project that uses PDF.js to display PDFs. Things had changed (like the introduction of a top-level await that Svelte doesn't like and a change in philosophy about the release of the default viewer's source code). Was I really going to have to generate the files on the server just so I have control over the filename? That's not acceptable.

Overview

Thankfully, you can now solve this problem with the Cache API in combination with the Service Worker API. They are widely supported by modern browsers, so there shouldn't be any issues in terms of compatibility. IE doesn't support it, obviously, but that's not really worth mentioning.

Unfortunately, this won't work well in Chromium-based browsers because of a years-old bug

This technique allows you to leverage browser-based rendering while maintaining control over the filename with the caveat that Chromium will only download the file directly from the server. Please star the bug report if you want to see this fixed in Chromium.

Cache API

Basically, you add the document to a cache and assign it a URL on your server that is won't get in the way of any other files, then set an iframe's src to the URL once the cache finishes accepting it. That stores it where your service worker can find it and tells the browser to go looking for it there. Since you define the URL, you also control the filename (it's the last segment of the pathname).

Service Worker API

Then you create and register a service worker that checks the cache to see if the file exists before fetching it from the Internet. If it's in your cache, it's because you put it there, so don't worry about it getting confused with random files. When the browser goes looking for the file that you stored a moment ago, it'll find and return it as if you downloaded it off the Internet.

Conclusion

Service workers are easy to register in SvelteKit just by creating a file in the correct location, but it really doesn't take much to create and register one with any other framework. You just have to keep in mind that the browser will try to hang onto the service worker, forcing you to clear your site data when you want to make changes to it. So it's best to avoid putting code in there that might change frequently.

Comments

2

I was looking for the same and actually you can add a download attribute for the link and that would make the trick on Chrome, I didn't tried in IE so far.

<a href="urlBlobData" download="fileTest.csv">Your file</a> 

and this is an example with Angular 5

<a #link *ngIf="urlBlobData" [href]="urlData" target="_blank" rel="noopener" download="fileTest.csv">Your file</a> 

Hope that work for you as well.

2 Comments

How to provide ngIf and download attribute's values? I am getting binary format file in an api response, target='_blank' in html its still not working, I tried removing download attribute too, what else is needed?
In Chrome, as soon as download is present on the anchor, it triggers a download instead of opening in a new tab.
1

There's another possible solution that can be explored but it has it's own issues. You can actually store your blob url in the browser cookie storage and then retrieve it from another tab and when you create the shared worker it will connect to the same worker. The caveat here is that your blob can disappear while your cookie value is still set so when a new worker gets initialized it will silently fail. There may be ways to mitigate this if you tested somehow to see if the worker script was running successfully and if it's not, erase the cookie and recreate the blob. However there doesn't seem to be a reliable way to know if workers are running from the main thread so this would be tricky, but I suspect doable if you use some delays/timeouts and wait for an echo from the worker for example.

Comments

1

Answer for 2025

It is 2025 and since the blob API is still a pain to work with and there is still no really good way to do this, I wasted a lot of time trying to find a solution that worked well on all the browsers out there and satisfied my client and this what I ended up with, I hope it helps someone else in saving sometime.

Solution: Preview File in New Tab with Download Option

This is the function that I ended up using on my app and in all my other projects for PDF viewing / downloading

function previewFile(response, filename, type = 'application/pdf') { const file = new Blob([response.data], { type }); const fileURL = URL.createObjectURL(file); // Open a new tab with the preview const newTab = window.open(); if (newTab) { newTab.document.write(` <html> <head> <title>${filename}</title> <style> body { margin: 0; padding: 0; } .download-btn { position: fixed; bottom: 20px; right: 20px; background-color: #007bff; color: white; border: none; padding: 10px 15px; font-size: 14px; border-radius: 5px; cursor: pointer; } .download-btn:hover { background-color: #0056b3; } </style> </head> <body> <iframe src="${fileURL}" style="width:100vw; height:100vh; border:none;"></iframe> <button class="download-btn" onclick="downloadFile()">Download</button> <script> function downloadFile() { const a = document.createElement('a'); a.href = "${fileURL}"; a.download = "${filename}"; document.body.appendChild(a); a.click(); document.body.removeChild(a); } // Change the URL displayed in the address bar window.history.pushState("", "${filename}", "/preview/${filename}"); </script> </body> </html> `); newTab.document.close(); } else { alert("Pop-up blocked! Please allow pop-ups for this site."); } } 

The idea is that instead of directly opening the blob in a new tab we open it in a iFrame inside that new tab which lets us do whatevere we want with the html/css/js of that new tab, so what I'm doing here is that I'm opening the blob in full width and height which gives the exact same visual as if it was opened directly and on top of that I added a download button with the usual trick that let's you download the blob with the correct Name.

Pros

  • We can set the url as we like ! (Finally !)
  • We can set the title of the page to the correct filename.
  • When we download using our button the filename is correct
  • We can change the css of the button however we like (we can even place it in a way where it hides the default browser's download button)

Cons

It is a great solution but it's still not optimal

  • The url is fake (Visual only) meaning if you copy/paste it somewhere else it won't work, if you refresh the page it won't work...
  • I'm not sure if the url change works on all browsers, please refer to this question How do I modify the URL without reloading the page?
  • If we download using the default browser's button it will not have the correct name (it will be the blob hash thing)

Hope this helps, thanks for reading.

Comments

-3

Taken from How to set a file name using window.open

Granted this is downloading the file not opening in another tab.

var downloadLink = document.createElement("a"); downloadLink.href = myBlob; downloadLink.download = "myFile.pdf"; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); 

Comments

-3

You can add/pass file name by adding below, however it will download the file instead of opening it in new tab, but at least user will get a properly named file,

//import and inject Renderer2 import { Renderer2 } from '@angular/core'; let downloadLink = this.renderer.createElement('a'); //this converts binary data to a pdf downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: 'application/pdf' })); //you can pass filename here downloadLink.setAttribute('download', filename); document.body.appendChild(downloadLink); downloadLink.click(); 

6 Comments

Just curious, why is this answer given negative rating? This worked for me, after trying lots of different things for whole two days. It might be helpful for a person facing same scenario as me.
What is downloadLink, and how does it relate to the original question?
@Pinka it gets downvoted because it doesn't solve the problem. Your solution downloads the document (I assume PDF) instead of showing it in a new tab (with ultimately the correct PDF name).
@Dankas Yes, it does download the pdf instead of opening it in new tab, but as mentioned, I just posted the solution of how we can pass/set the name of the file as the original question had that issue of not being able to set name. It is just a workaround to set name. Sorry, I'll update my answer to inform same.
@Pinka also your answer is a little late. Other answers already mentioned this solution years ago
|
-4

You can simply add a setTimeout to change the page title after the blob has been loaded in the new tab like this -

newTab.location.href = URL.createObjectURL(blob); setTimeout(function() { newTab.document.title = blob.name; }, 10); 

2 Comments

SecurityError: Permission denied to access property "document" on cross-origin object
updating title wont work

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.