Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Download file from MVC 5 to Angular 2

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?

like image 789
Jakob Lithner Avatar asked Feb 01 '17 16:02

Jakob Lithner


3 Answers

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.

like image 180
peinearydevelopment Avatar answered Nov 12 '22 14:11

peinearydevelopment


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);
}
like image 36
Jakob Lithner Avatar answered Nov 12 '22 16:11

Jakob Lithner


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'/>
like image 1
Mohtisham Zubair Avatar answered Nov 12 '22 14:11

Mohtisham Zubair