8

I have experience in C# backend and ASP.Net MVC. Now I am making my first attempt on Angular 2. It takes time but I like most of it. Now I am stuck on a simple file download.

I have read all examples that i found here on Stackoverflow, but I still don't get my example to work.

On server side I have this C# code:

 public ActionResult DownloadPicture(long id) { var bytes = System.IO.File.ReadAllBytes("images\dummy.jpg"); return GetAttachement(bytes, "DummyFile.jpg"); } private ActionResult GetAttachement(byte[] bytes, string fileName) { var contentType = MimeMapping.GetMimeMapping(fileName); var contentDisposition = new System.Net.Mime.ContentDisposition { FileName = fileName, Inline = true }; Response.AppendHeader("Content-Disposition", contentDisposition.ToString()); return File(bytes, contentType); } 

On client side I have this Typescript code:

public pictureDownload(id: number): void { let options = new RequestOptions({ search: new URLSearchParams("id=" + id) }); this.http.get(this.urlPictureDownload, options).subscribe((data: any) => { // var blob = new Blob([data._body], { type: "image/jpeg" }); // var url = window.URL.createObjectURL(blob); // window.open(url); }); } 

The request is coming in to server side. The array is downloaded. I guess my problem lies on client side. Can anyone help me out?

4
  • 1
    Interesting - I haven't tried this with FileSaver.js. Note arraybuffer. Hth... Commented Feb 1, 2017 at 16:56
  • Is there a reason you can't just use a plain old link for the file? That's probably easiest. Commented Feb 1, 2017 at 18:30
  • I know, I really would like that but the files are not located on disk. They are stored in backend storage so I actually get hold of them as a stream. Commented Feb 2, 2017 at 7:09
  • Thanks EsDF for the link. It really helped. I still have some questions on the download though. See Update above. Commented Feb 2, 2017 at 7:59

3 Answers 3

2

For whatever its worth, this isn't just an issue with Angular2. This seems to be an 'issue' with the browsers(or possibly a spec). There are a few different options you have with this approach that I'm aware of.

  1. Once the bytearray is returned to the browser, you could do something like this: var pom = document.createElement('a'); pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(data)); pom.setAttribute('download', 'PICTURENAME.jpeg'); pom.style.display = 'none'; document.body.appendChild(pom); pom.click(); document.body.removeChild(pom);

  2. The other approach I'm aware of is to create the file on the server at a temp path and then return a redirect result to the user with an id identifying that file(often times a GUID) and have that route that is redirected to return your file result.

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

Comments

2

For all future readers I sum it all up here. By suggestion from EsDF with suggestion from peinearydevelopment I now have a working solution. Specifying the responseType as ArrayBuffer in the request was the most crucial part and the download trick was really helpful.

Alt 1: I think Casey is right. Whenever possible the easiest way is to just use a simple link tag that points to a server resource. When as in my case, that is not an option, any of the other two alternatives will be useful.

Alt 2: The method openPictureInNewWindow is the simple and straightforward approach. The downside is that it will present an odd url that looks like this: blob:http://localhost/037713d1-a8b9-4fe3-8c43-ee5998ffdc5d.

Alt 3: The other method downloadFile will go one step further and create a temporary link tag and make use of it for file download. That will for most users look like the normal approach. But it doesn't feel like an "Angular 2 approach" manipulating the DOM, but it is a more user friendly way, so for now that is the one I will use. Thanks for all good input!

public pictureDownload(id: number) { let options = new RequestOptions({ search: new URLSearchParams("id=" + id), responseType: ResponseContentType.ArrayBuffer }); this.http.get(this.urlPictureDownload, options).subscribe((data: any) => { //this.openPictureInNewWindow(data._body, "image/jpeg"); this.downloadFile(data._body, "Screenshot_" + id + ".jpg", "image/jpeg"); }); } private openPictureInNewWindow(data: ArrayBuffer, contentType: string) { var blob = new Blob([data], { type: contentType }); var url = window.URL.createObjectURL(blob); window.open(url); } private downloadFile(data: ArrayBuffer, fileName: string, contentType: string) { var blob = new Blob([data], { type: contentType }); var url = window.URL.createObjectURL(blob); var link = document.createElement("a"); link.setAttribute("href", url); link.setAttribute("download", fileName); link.style.display = "none"; document.body.appendChild(link); link.click(); document.body.removeChild(link); } 

1 Comment

I did have difficulties with this in angular1 as well. I did get my images as bytearray from db, tunnelled them through webapi as json and inserted the outcome in an img src tag. (worked) This had however the issue, that the json became rather big and no client caching occurred. (i used this approach to get small avater images, so the topic was left for future improvements).
1

If you are seeing an image in browser for your backend url of application. Then you could directly assign it to src of img in markup of component like this:

suppose you declare a field with imgURL in component and initialize it with your backend image action method. then in markup you could do so

<img src='{{imgURL}}' alt='any message'/> 

1 Comment

I probably shold have been more specific on this. I would love to use a simple url, but the files are not located on disk. They are stored in backend storage so I actually get hold of them as a stream.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.