Trying to download file from server using angularJS and c# webapi

All we need is an easy explanation of the problem, so here it is.

When uploading files to the server it goes great; no damaged files. However when i download files ( other than pure txt:s they woork)
they grow in size and become corrupt. After alot of investigation I dont know what could be wrong. Im simply writing the file
as a stream to the response and downloading the blob.
Any ideas are welcome!

Relied heavily on this Thread for the solution ; Download file from an ASP.NET Web API method using AngularJS

Current Code below;

WebApi:

    [Route("GetFile")]
public HttpResponseMessage GetFile()
{
    HttpResponseMessage result = null;
    //Get file object here
    try 
    {
        IEnumerable<string> headerValues = Request.Headers.GetValues("fileID");
        int key = Int32.Parse(headerValues.FirstOrDefault());
        var fetchFile = db.FileRecords.Single(a => a.id == key);

        var localFilePath = fetchFile.path + fetchFile.name;

        if (!System.IO.File.Exists(localFilePath))
        {
            result = Request.CreateResponse(HttpStatusCode.Gone);
        }
        else
        {// serve the file to the client
            //I have used the x-filename header to send the filename. This is a custom header for convenience.
            //You should set the content-type mime header for your response too, so the browser knows the data format.
            var info = System.IO.File.GetAttributes(localFilePath);
            result = Request.CreateResponse(HttpStatusCode.OK);
            result.Content = new StreamContent(new FileStream(localFilePath, FileMode.Open, FileAccess.Read));
            result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            result.Content.Headers.Add("x-filename", fetchFile.name);
            result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
            result.Content.Headers.ContentDisposition.FileName = fetchFile.name;
        }
        return result;
    }
    catch (Exception e)
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
}

View:

<button type="button" class="btn btn-default btn-sm" data-localize="DOWNLOAD" ng-click="downloadFiles(file)">
    Download file
</button>

Controller:

    /******** FILE DOWNLOAD  **********/
$scope.downloadFiles = function (file) {
        $http({
            method: 'GET',
            cache: false,
            url: host + 'api/Files/GetFile',
            headers: {
                'Content-Type': 'application/json; charset=utf-8',
                'fileID': file.id
            }
        }).success(function (data, status, headers) {
            var octetStreamMime = 'application/octet-stream';
            var success = false;

            // Get the headers
            headers = headers();

            // Get the filename from the x-filename header or default to "download.bin"
            var filename = headers['x-filename'] || 'download.bin';

            // Determine the content type from the header or default to "application/octet-stream"
            var contentType = headers['content-type'] || octetStreamMime;

            try {

                console.log(filename);
                // Try using msSaveBlob if supported
                console.log("Trying saveBlob method ...");
                var blob = new Blob([data], { type: contentType });
                if (navigator.msSaveBlob)
                    navigator.msSaveBlob(blob, filename);
                else {
                    // Try using other saveBlob implementations, if available
                    var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
                    if (saveBlob === undefined) throw "Not supported";
                    saveBlob(blob, filename);
                }
                console.log("saveBlob succeeded");
                success = true;
            } catch (ex) {
                console.log("saveBlob method failed with the following exception:");
                console.log(ex);
            }

            if (!success) {
                // Get the blob url creator
                var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
                if (urlCreator) {
                    // Try to use a download link
                    var link = document.createElement('a');
                    if ('download' in link) {
                        // Try to simulate a click
                        try {
                            // Prepare a blob URL
                            console.log("Trying download link method with simulated click ...");
                            var blob = new Blob([data], { type: contentType });
                            var url = urlCreator.createObjectURL(blob);
                            link.setAttribute('href', url);

                            // Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
                            link.setAttribute("download", filename);

                            // Simulate clicking the download link
                            var event = document.createEvent('MouseEvents');
                            event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
                            link.dispatchEvent(event);
                            console.log("Download link method with simulated click succeeded");
                            success = true;

                        } catch (ex) {
                            console.log("Download link method with simulated click failed with the following exception:");
                            console.log(ex);
                        }
                    }

                    if (!success) {
                        // Fallback to window.location method
                        try {
                            // Prepare a blob URL
                            // Use application/octet-stream when using window.location to force download
                            console.log("Trying download link method with window.location ...");
                            var blob = new Blob([data], { type: octetStreamMime });
                            var url = urlCreator.createObjectURL(blob);
                            window.location = url;
                            console.log("Download link method with window.location succeeded");
                            success = true;
                        } catch (ex) {
                            console.log("Download link method with window.location failed with the following exception:");
                            console.log(ex);
                        }
                    }

                }
            }

            if (!success) {
                // Fallback to window.open method
                console.log("No methods worked for saving the arraybuffer, using last resort window.open");
                window.open(httpPath, '_blank', '');
            }
            /******************/


        }).error(function (data, status) {

            console.log("Request failed with status: " + status);

            // Optionally write the error out to scope
            //$scope.errorDetails = "Request failed with status: " + status;
        });
}

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Added arraybuffer as a responsetype of the GET request, now the browser interprets the files correctly.

    /******** FILE DOWNLOAD  **********/
$scope.downloadFiles = function (file) {
        $http({
            method: 'GET',
            cache: false,
            url: host + 'api/Files/GetFile',
            responseType:'arraybuffer',
            headers: {
                'Content-Type': 'application/json; charset=utf-8',
                'fileID': file.id
            }

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply