Жуэль Итуа
Жуэль Итуа

Reputation: 199

How to correctly download files to Angular from ASP.NET Core?

I have a file on the server that I want to send to the client:

[HttpPost]
public IActionResult Test()
{
    byte[] bytes = System.IO.File.ReadAllBytes(Path.Combine(FilesFolder, "test.docx"));
    return File(bytes, _contentTypeWord);
}

I also tried with

return PhysicalFile(pathUploadFile, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");

At the client I accept using:

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);
}

public test() {
    this.http.post("Diplom/Test", { }, {
        headers: this.headers(),
    }).subscribe(
        result => {
            this._downloadFile(result.arrayBuffer(), "test.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
        },
        error => {
            alert("Не удалось отредактировать файл");
            console.error(JSON.stringify(error));
        })
}

The file that the client receives is corrupt and does not open. The file that is on the server is fine and opens perfectly. The resulting file still does not open, and even weighs 2 times more (on the server 487 KB, and the client has 925 KB).

Upvotes: 8

Views: 38250

Answers (3)

Bharathkumar V
Bharathkumar V

Reputation: 357

To download any file, first install the following,

npm install rxjs --save
npm install file-saver --save

Include these packages in your component(Angular 2+),

import { Observable } from 'rxjs';
import { saveAs } from 'file-saver';
import { map } from "rxjs/operators";
import { Subject } from 'rxjs';
import { HttpEventType, HttpClient } from '@angular/common/http';

Front-end code,

<input type="button" placeholder="Download" value="Download" (click)="download(file)" />

Angular Code,

download(file) {
        let fileName = file;
        let checkFileType = fileName.split('.').pop();
        var fileType;
        if (checkFileType == ".txt") {
            fileType = "text/plain";
        }
        if (checkFileType == ".pdf") {
            fileType = "application/pdf";
        }
        if (checkFileType == ".doc") {
            fileType = "application/vnd.ms-word";
        }
        if (checkFileType == ".docx") {
            fileType = "application/vnd.ms-word";
        }
        if (checkFileType == ".xls") {
            fileType = "application/vnd.ms-excel";
        }
        if (checkFileType == ".png") {
            fileType = "image/png";
        }
        if (checkFileType == ".jpg") {
            fileType = "image/jpeg";
        }
        if (checkFileType == ".jpeg") {
            fileType = "image/jpeg";
        }
        if (checkFileType == ".gif") {
            fileType = "image/gif";
        }
        if (checkFileType == ".csv") {
            fileType = "text/csv";
        }
        this.DownloadFile(fileName, fileType)
            .subscribe(
                success => {
                    saveAs(success, fileName);
                },
                err => {
                    alert("Server error while downloading file.");
                }
            );
    }

    DownloadFile(filePath: string, fileType: string): Observable<any> {

        let fileExtension = fileType;
        let input = filePath;

        return this.http.get(this.yourApiUrl + "?fileName=" + input, {
            responseType: 'blob',
            observe: 'response'
        })
            .pipe(
                map((res: any) => {
                    return new Blob([res.body], { type: fileExtension });
                })
            );
    }

.NET Core API code,

        [HttpGet]
        public async Task<FileStream> DownloadFile(string fileName)
        {
            return new FileStream(file, FileMode.Open, FileAccess.Read);
        }

Upvotes: 0

alsami
alsami

Reputation: 9815

You can use a file result and just provide an api link that will return a FileContentResult.

public IActionResult Download(// some parameter) 
{
   // get the file and convert it into a bytearray
   var locatedFile = ....
   return new FileContentResult(locatedFile, new 
       MediaTypeHeaderValue("application/octet"))
       {
           FileDownloadName = "SomeFileDownloadName.someExtensions"
       };
}

Now you only need to provide the link and browser will know how to handle it. No need to do it yourself then.

Edit: I just tested this approach with angular, you need to do the following to download the file when using angulars HttpClient.

First you need to install file-saver via npm.

npm i --save file-saver

Then in your module import and include HttpClientModule

import { HttpClientModule } from '@angular/common/http';
...
@NgModule({
   imports: [
      HttpClientModule
      ...
   ]
...
})

Now go ahead to your service and do the following

import { HttpClient } from '@angular/common/http';
import { saveAs } from 'file-saver';

@Injectable()
export class MyFileService {

   public constructor(private http: HttpClient) {}

       public downloadFile() {
    this.http.get('my-api-url', { responseType: 'blob' }).subscribe(blob => {
       saveAs(blob, 'SomeFileDownloadName.someExtensions', {
          type: 'text/plain;charset=windows-1252' // --> or whatever you need here
       });
    });
}

Then blob is handled and a file-save dialog is created.

Upvotes: 18

Жуэль Итуа
Жуэль Итуа

Reputation: 199

I do not know why the option specified in the question does not work. But I found a solution, so to say "in another forest". I'm not very versed, so I'll just describe the process of solving the problem. For the beginning I will immediately say that the problem was on the client's side. The method on the server worked correctly from the start. I decided to use another method to download the files "fetch". And came across the next post in the forum. From there I took the following answer

this.httpClient
    .fetch(url, {method, body, headers})
    .then(response => response.blob())
    .then(blob => URL.createObjectURL(blob))
    .then(url => {
        window.open(url, '_blank');
        URL.revokeObjectURL(url);
    });

He did not work for me either. I changed it so

    fetch(url, {
            method: 'POST',
            headers: this.headers()
        })
        .then(response => response.blob())
        .then(blob => URL.createObjectURL(blob))
        .then(url => {
            window.open(url, '_blank');
        });

Because of this line nothing happened

URL.revokeObjectURL(url);

This option worked, but with screwed. He saved the file with a strange name and no extension. Then I changed it so.

    fetch(url, {
            method: 'POST',
            headers: this.headers()
        })
            .then(response => response.blob())
            .then(blob => URL.createObjectURL(blob))
            .then(url => {
                var link = document.createElement("a");
                link.setAttribute("href", url);
                link.setAttribute("download", "test.docx");
                link.style.display = "none";
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            });

And it works! I apologize for the English. I'm using Google translator

Upvotes: 7

Related Questions