У меня есть приложение WebApi / MVC, для которого я разрабатываю клиент angular2 (чтобы заменить MVC). У меня проблемы с пониманием того, как Angular сохраняет файл.

Запрос в порядке (отлично работает с MVC, и мы можем регистрировать полученные данные), но я не могу понять, как сохранить загруженные данные (я в основном следую той же логике, что и в этом сообщении). Я уверен, что это глупо просто, но пока просто не понимаю.

Ниже приведен код функции компонента. Я пробовал разные альтернативы, способ blob должен быть подходящим, насколько я понял, но нет функции createObjectURL в URL. Я даже не могу найти определение URL в окне, но, видимо, оно существует. Если я использую FileSaver.js модуль, я получаю ту же ошибку. Так что я полагаю, что это то, что недавно изменилось или еще не реализовано. Как запустить сохранение файла в A2?

файл загрузки (тип: строка) {

    пусть thefile = {};
    this.pservice.downloadfile (this.rundata.name, тип)
        .subscribe (data => thefile = new Blob ([data], {type: "application / octet-stream"}), //console.log(data),
                    error => console.log («Ошибка загрузки файла.»),
                    () => console.log ('Загрузка файла завершена.'));

    пусть url = window.URL.createObjectURL (файл);
    window.open (URL);
}

Для полноты информации ниже приведена служба, которая извлекает данные, но единственное, что она делает, это отправляет запрос и передает данные без сопоставления, если это удается:

файл загрузки (имя запуска: строка, тип: строка) {
   вернуть this.authHttp.get (this.files_api + this.title + "/" + runname + "/? file =" + type)
            .catch (this.logAndPassOn);
}

Ответы (29)

Проблема в том, что наблюдаемый запускается в другом контексте, поэтому, когда вы пытаетесь создать URL-адрес var, вы получаете пустой объект, а не нужный вам blob.

Один из многих способов решить эту проблему:

this._reportService.getReport (). Subscribe (data => this.downloadFile (data)), // console.log (данные),
                 error => console.log ('Ошибка при загрузке файла.'),
                 () => console.info ('ОК');

Когда запрос будет готов, он вызовет функцию «downloadFile», которая определена следующим образом:

downloadFile (data: Response) {
  const blob = новый Blob ([данные], {type: 'text / csv'});
  const url = window.URL.createObjectURL (blob);
  window.open (URL);
}

blob был создан идеально, поэтому URL-адрес var, если не открывается новое окно, убедитесь, что вы уже импортировали 'rxjs / Rx';

import 'rxjs / Rx';

Надеюсь, это вам поможет.

Ну, я написал фрагмент кода, вдохновленный многими из приведенных выше ответов, который должен легко работать в большинстве сценариев, когда сервер отправляет файл с заголовком размещения содержимого без каких-либо сторонних установок, кроме rxjs и angular.

Во-первых, как вызвать код из файла вашего компонента

this.httpclient.get(
   `${myBackend}`,
   {
      observe: 'response',
      responseType: 'blob'
   }
).pipe(first())
.subscribe(response => SaveFileResponse(response, 'Custom File Name.extension'));

Как видите, это в основном средний бэкэнд-вызов из angular с двумя изменениями

  1. Наблюдаю реакцию вместо тела
  2. Я прямо говорю о том, что ответ - это капля

После получения файла с сервера я, в принципе, делегирую всю задачу сохранения файла вспомогательной функции, которую я сохраняю в отдельном файле, и импортирую в любой компонент, который мне нужен для

export const SaveFileResponse = 
(response: HttpResponse, 
 filename: string = null) => 
{
    //null-checks, just because :P
    if (response == null || response.body == null)
        return;

    let serverProvidesName: boolean = true;
    if (filename != null)
        serverProvidesName = false;

    //assuming the header is something like
    //content-disposition: attachment; filename=TestDownload.xlsx; filename*=UTF-8''TestDownload.xlsx
    if (serverProvidesName)
        try {
            let f: string = response.headers.get('content-disposition').split(';')[1];
            if (f.includes('filename='))
                filename = f.substring(10);
        }
        catch { }
    SaveFile(response.body, filename);
}

//Create an anchor element, attach file to it, and
//programmatically click it. 
export const SaveFile = (blobfile: Blob, filename: string = null) => {
    const a = document.createElement('a');
    a.href = window.URL.createObjectURL(blobfile);
    a.download = filename;
    a.click();
}

Больше никаких загадочных имен файлов GUID! Мы можем использовать любое имя, предоставленное сервером, без необходимости явно указывать его в клиенте, или перезаписать имя файла, предоставленное сервером (как в этом примере). Кроме того, при необходимости можно легко изменить алгоритм извлечения имени файла из Content-Disposition в соответствии со своими потребностями, и все остальное останется неизменным - в случае ошибки во время такого извлечения он просто передаст 'null' в качестве имени файла.

Как уже указывалось в другом ответе, IE, как всегда, требует особого обращения. Но с появлением хрома Edge через несколько месяцев я бы не стал беспокоиться об этом при создании новых приложений (надеюсь). Существует также проблема отзыва URL-адреса, но я не совсем уверен в этом, поэтому, если бы кто-то мог помочь с этим в комментариях, это было бы здорово.

Как упоминал Алехандро Корредор, это простая ошибка осциллографа. subscribe запускается асинхронно, и open должен быть помещен в этот контекст, чтобы данные завершили загрузку, когда мы инициируем загрузку.

Тем не менее, есть два способа сделать это. Как рекомендуют документы, служба позаботится о получении и сопоставлении данных:

// На сервисе:
downloadfile (runname: string, type: string) {
  var заголовки = новые заголовки ();
  headers.append ('responseType', 'arraybuffer');
  вернуть this.authHttp.get (this.files_api + this.title + "/" + runname + "/? file =" + type)
            .map (res => новый Blob ([res], {type: 'application / vnd.openxmlformats-officedocument.spreadsheetml.sheet'}))
            .catch (this.logAndPassOn);
}

Затем на компоненте мы просто подписываемся и работаем с отображенными данными. Есть две возможности.Первый, как предложено в исходном сообщении, но требует небольшого исправления, как отметил Алехандро:

// На компоненте
downloadfile (type: string) {
  this.pservice.downloadfile (this.rundata.name, тип)
      .subscribe (данные => window.open (window.URL.createObjectURL (данные)),
                  error => console.log («Ошибка загрузки файла.»),
                  () => console.log ('Загрузка файла завершена.'));
  }

Второй способ - использовать FileReader. Логика такая же, но мы можем явно дождаться, пока FileReader загрузит данные, избегая вложенности и решая проблему асинхронности.

// На компоненте, использующем FileReader
downloadfile (type: string) {
    var reader = new FileReader ();
    this.pservice.downloadfile (this.rundata.name, тип)
        .subscribe (res => reader.readAsDataURL (res),
                    error => console.log («Ошибка загрузки файла.»),
                    () => console.log ('Загрузка файла завершена.'));

    reader.onloadend = function (e) {
        window.open (reader.result, 'Excel', 'width = 20, height = 10, toolbar = 0, menubar = 0, scrollbars = no');
  }
}

Примечание: Я пытаюсь загрузить файл Excel, и, несмотря на то, что загрузка инициирована (это ответ на вопрос), файл поврежден.См. Ответ на этот пост для , чтобы избежать повреждения файла.

Как насчет этого?

this.http.get (targetUrl, {responseType: ResponseContentType.Blob})
        .catch ((err) => {вернуть [сделай сам]})
        .subscribe ((res: Response) => {
          var a = document.createElement ("а");
          a.href = URL.createObjectURL (res.blob ());
          a.download = имя_файла;
          // начать загрузку
          a.click ();
        })

Я бы справился.
не нужен дополнительный пакет.

У меня есть решение для загрузки с angular 2 без повреждения, используя пружинный мвк и угловой 2

1-й - мой тип возврата: -ResponseEntity с конца java. Здесь я отправляю массив byte [] с типом, возвращаемым контроллером.

2-й - для включения файловой системы в вашу рабочую область - на странице индекса как:


3-й - в компоненте ts напишите этот код:

импорт {ResponseContentType} из '@ angular.core';

let headers = new Headers ({'Content-Type': 'application / json', 'MyApp-Application': 'AppName', 'Accept': 'application / pdf'});
        let options = new RequestOptions ({заголовки: заголовки, responseType: ResponseContentType.Blob});
            this.http
            .post ('/ проект / тест / экспорт',
                    somevalue, варианты)
              .subscribe (data => {

                  var mediaType = 'приложение / vnd.ms-excel';
                  пусть blob: Blob = data.blob ();
                    окно ['saveAs'] (blob, 'sample.xls');

                });

Это даст вам формат файла xls. Если вы хотите использовать другие форматы, измените тип медиафайла и имя файла с правильным расширением.

Загрузить файл

my_url должен иметь то же происхождение, иначе он будет перенаправлен в это место

Если вы отправляете параметры только по URL-адресу, вы можете сделать это следующим образом:

downloadfile(runname: string, type: string): string {
   return window.location.href = `${this.files_api + this.title +"/"+ runname + "/?file="+ type}`;
}

в сервисе, который получает параметры

Сегодня я столкнулся с тем же случаем, мне пришлось загрузить файл pdf в качестве вложения (файл не должен отображаться в браузере, а загружаться вместо этого). Для этого я обнаружил, что мне нужно получить файл в формате Angular Blobи в то же время добавить в ответ заголовок Content-Disposition.

Это было самое простое, что я смог получить (Angular 7):

Внутри сервиса:

getFile (id: String): Observable > {
  return this.http.get (`./file / $ {id}`, {responseType: 'blob', обратите внимание: 'response'});
}

Затем, когда мне нужно загрузить файл в компоненте, я могу просто:

fileService.getFile ('123'). Subscribe ((file: HttpResponse ) => window.location.href = file.url);

ОБНОВЛЕНИЕ:

Удалена ненужная настройка заголовка из сервиса

Angular 12 + ASP.NET 5 WEB API

Вы можете вернуть объект Blob с сервера и создать тег привязки и установить свойство href для URL-адреса объекта, созданного из Blob. Теперь нажатие на якорь загрузит файл. Вы также можете указать имя файла.

downloadFile(path: string): Observable {
        return this._httpClient.post(`${environment.ApiRoot}/accountVerification/downloadFile`, { path: path }, {
            observe: 'response',
            responseType: 'blob'
        });
    }

saveFile(path: string, fileName: string): void {
            this._accountApprovalsService.downloadFile(path).pipe(
                take(1)
            ).subscribe((resp) => {
                let downloadLink = document.createElement('a');
                downloadLink.href = window.URL.createObjectURL(resp.body);
                downloadLink.setAttribute('download', fileName);
                document.body.appendChild(downloadLink);
                downloadLink.click();
                downloadLink.remove();
            });
            
        }

Бэкэнд

[HttpPost]
[Authorize(Roles = "SystemAdmin, SystemUser")]
public async Task DownloadFile(FilePath model)
{
    if (ModelState.IsValid)
    {
        try
        {
            var fileName = System.IO.Path.GetFileName(model.Path);
            var content = await System.IO.File.ReadAllBytesAsync(model.Path);
            new FileExtensionContentTypeProvider()
                .TryGetContentType(fileName, out string contentType);
            return File(content, contentType, fileName);
        }
        catch
        {
            return BadRequest();
        }
    }

    return BadRequest();

}

Я нашел ответы, на которые пока не хватало понимания, а также предупреждений. Вы можете и должны следить за несовместимостью с IE10 + (если вам интересно).

Это полный пример с прикладной частью и служебной частью после. Обратите внимание, что мы устанавливаем наблюдение: «ответ», чтобы перехватить заголовок для имени файла. Также обратите внимание, что заголовок Content-Disposition должен быть установлен и предоставлен сервером, иначе текущий Angular HttpClient не передаст его. Я добавил фрагмент кода dotnet core для этого ниже.

public exportAsExcelFile(dataId: InputData) {
    return this.http.get(this.apiUrl + `event/export/${event.id}`, {
        responseType: "blob",
        observe: "response"
    }).pipe(
        tap(response => {
            this.downloadFile(response.body, this.parseFilename(response.headers.get('Content-Disposition')));
        })
    );
}

private downloadFile(data: Blob, filename: string) {
    const blob = new Blob([data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8;'});
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
    } else {
        const link = document.createElement('a');
        if (link.download !== undefined) {
            // Browsers that support HTML5 download attribute
            const url = URL.createObjectURL(blob);
            link.setAttribute('href', url);
            link.setAttribute('download', filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}

private parseFilename(contentDisposition): string {
    if (!contentDisposition) return null;
    let matches = /filename="(.*?)"/g.exec(contentDisposition);

    return matches && matches.length > 1 ? matches[1] : null;
}

Ядро Dotnet с Content-Disposition и MediaType

 private object ConvertFileResponse(ExcelOutputDto excelOutput)
    {
        if (excelOutput != null)
        {
            ContentDisposition contentDisposition = new ContentDisposition
            {
                FileName = excelOutput.FileName.Contains(_excelExportService.XlsxExtension) ? excelOutput.FileName : "TeamsiteExport.xlsx",
                Inline = false
            };
            Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
            Response.Headers.Add("Content-Disposition", contentDisposition.ToString());
            return File(excelOutput.ExcelSheet, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        }
        else
        {
            throw new UserFriendlyException("The excel output was empty due to no events.");
        }
    }
 let headers = new Headers({
                'Content-Type': 'application/json',
                'MyApp-Application': 'AppName',
                'Accept': 'application/vnd.ms-excel'
            });
            let options = new RequestOptions({
                headers: headers,
                responseType: ResponseContentType.Blob
            });


this.http.post(this.urlName + '/services/exportNewUpc', localStorageValue, options)
                .subscribe(data => {
                    if (navigator.appVersion.toString().indexOf('.NET') > 0)
                    window.navigator.msSaveBlob(data.blob(), "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+".xlsx");

                    else {
                        var a = document.createElement("a");
                        a.href = URL.createObjectURL(data.blob());
                        a.download = "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+ ".xlsx";
                        a.click();
                    }
                    this.ui_loader = false;
                    this.selectedexport = 0;
                }, error => {
                    console.log(error.json());
                    this.ui_loader = false;
                    document.getElementById("exceptionerror").click();
                });

Просто введите url как href, как показано ниже.

Download File

Обновите ответ Гектора с помощью файловой заставки и HttpClient для шага 2:

public downloadFile(file: File): Observable {
    return this.http.get(file.fullPath, {responseType: 'blob'})
}

Если вкладка открывается и закрывается, ничего не загружая, я пробовал следовать с фиктивной ссылкой привязки, и это сработало.

downloadFile(x: any) {
var newBlob = new Blob([x], { type: "application/octet-stream" });

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(newBlob);
      return;
    }

    // For other browsers: 
    // Create a link pointing to the ObjectURL containing the blob.
    const data = window.URL.createObjectURL(newBlob);

    var link = document.createElement('a');
    link.href = data;
    link.download = "mapped.xlsx";
    // this is necessary as link.click() does not work on the latest firefox
    link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));

    setTimeout(function () {
      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(data);
      link.remove();
    }, 100);  }

Вы также можете загрузить файл прямо из вашего шаблона, где вы используете атрибут загрузки, а в [attr.href] вы можете указать значение свойства из компонента. Это простое решение должно работать в большинстве браузеров.


Ссылка: https://www.w3schools.com/tags/att_a_download.asp

Чтобы загрузить и показать файлы PDF, очень похожий фрагмент кода выглядит следующим образом:

 частный файл загрузки (данные: ответ): void {
    let blob = new Blob ([data.blob ()], {type: "application / pdf"});
    пусть url = window.URL.createObjectURL (blob);
    window.open (URL);
  }

  public showFile (fileEndpointPath: string): void {
    пусть reqOpt: RequestOptions = this.getAcmOptions (); // getAcmOptions - наш вспомогательный метод. Измените эту строку в соответствии с нужными заголовками запроса.
    reqOpt.responseType = ResponseContentType.Blob;
    this.http
      .get (fileEndpointPath, reqOpt)
      .подписываться(
        data => this.downloadFile (данные),
        error => alert («Ошибка загрузки файла!»),
        () => console.log («ОК!»)
      );
  }

Вот что я сделал в моем случае -

// service method
downloadFiles(vendorName, fileName) {
    return this.http.get(this.appconstants.filesDownloadUrl, { params: { vendorName: vendorName, fileName: fileName }, responseType: 'arraybuffer' }).map((res: ArrayBuffer) => { return res; })
        .catch((error: any) => _throw('Server error: ' + error));
}

// a controller function which actually downloads the file
saveData(data, fileName) {
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    let blob = new Blob([data], { type: "octet/stream" }),
        url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
}

// a controller function to be called on requesting a download
downloadFiles() {
    this.service.downloadFiles(this.vendorName, this.fileName).subscribe(data => this.saveData(data, this.fileName), error => console.log("Error downloading the file."),
        () => console.info("OK"));
}

Ссылка на решение - здесь

Загрузите решение * .zip для angular 2.4.x: вы должны импортировать ResponseContentType из '@ angular / http' и изменить responseType на ResponseContentType.ArrayBuffer (по умолчанию это ResponseContentType.Json)

getZip (путь: строка, параметры: URLSearchParams = new URLSearchParams ()): Observable  {
 let headers = this.setHeaders ({
      'Content-Type': 'application / zip',
      'Принять': 'приложение / zip'
    });

 return this.http.get (`$ {environment.apiUrl} $ {path}`, {
   заголовки: заголовки,
   поиск: параметры,
   responseType: ResponseContentType.ArrayBuffer // магия
 })
          .catch (this.formatErrors)
          .map ((res: Response) => res ['_ body']);
}

Будет лучше, если вы попробуете вызвать новый метод внутри себя подписаться

this._reportService.getReport()
    .subscribe((data: any) => {
        this.downloadFile(data);
    },
        (error: any) => сonsole.log(error),
        () => console.log('Complete')
    );

Внутри downloadFile (data) функция, которую нам нужно сделать блок, ссылку, href и имя файла

downloadFile(data: any, type: number, name: string) {
    const blob = new Blob([data], {type: 'text/csv'});
    const dataURL = window.URL.createObjectURL(blob);

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(blob);
      return;
    }

    const link = document.createElement('a');
    link.href = dataURL;
    link.download = 'export file.csv';
    link.click();

    setTimeout(() => {

      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(dataURL);
      }, 100);
    }
}

У меня сработал следующий код

Сделал HTML так:


JS выглядит следующим образом:

let someData = {};
someData.name = 'someName';
someData.fileurl= 'someUrl';

function startDownload(someData){
    let link = document.createElement('a');
    link.href = someData.fileurl; //data is object received as response
    link.download = someData.fileurl.substr(someData.fileurl.lastIndexOf('/') + 1);
    link.click();
}

Если вам не нужно добавлять заголовки в запрос, чтобы загрузить файл в Angular2, вы можете сделать простой (ПРИНЦИП KISS):

window.location.href = 'http://example.com/myuri/report?param=x';

в вашем компоненте.

Этот ответ предполагает, что вы не можете загружать файлы напрямую с помощью AJAX, в первую очередь по соображениям безопасности. Итак, я опишу, что я делаю в этой ситуации,

01. Добавьте атрибут href в свой тег привязки внутри файла component.html,
например: -


02. Выполните все следующие шаги в вашем component.ts, чтобы обойти уровень безопасности и открыть диалоговое окно «Сохранить как всплывающее окно»,
например: -

import { environment } from 'environments/environment';
import { DomSanitizer } from '@angular/platform-browser';
export class ViewHrApprovalComponent implements OnInit {
private apiUrl = environment.apiUrl;
  fileUrl
 constructor(
    private sanitizer: DomSanitizer,
    private letterService: LetterService) {}
getGenaratedLetterTemplate(letter) {

    this.data.getGenaratedLetterTemplate(letter.letterId).subscribe(
      // cannot download files directly with AJAX, primarily for security reasons);
    console.log(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
    this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
  }

Примечание. Этот ответ будет работать, если вы получите сообщение об ошибке «ОК» с кодом состояния 200

Поделюсь решением, которое мне помогло (любые улучшения приветствуются)

на вашем сервисе 'pservice':

getMyFileFromBackend (typeName: string): Observable  {
    let param = new URLSearchParams ();
    param.set ('тип', имя_типа);
    // установка 'responseType: 2' сообщает angular, что вы загружаете буфер массива
    вернуть this.http.get (http://MYSITE/API/FILEIMPORT, {search: params, responseType: 2})
            .map (res => res.text ())
            .catch ((error: any) => Observable.throw (error || 'Ошибка сервера'));
}

Компонент Деталь:

файл загрузки (тип: строка) {
   this.pservice.getMyFileFromBackend (typename) .subscribe (
                    res => this.extractData (res),
                    (error: any) => Observable.throw (error || 'Ошибка сервера')
                );
}

extractData (res: string) {
    // преобразовать ответ в blob
    let myBlob: Blob = new Blob ([res], {type: 'application / vnd.oasis.opendocument. spreadsheet'}); // заменить тип любым типом вашего ответа

    var fileURL = URL.createObjectURL (myBlob);
    // Здесь скрестите пальцы и молитесь, как бы вы ни молились
    window.open (fileURL);
}

По компонентной части вы вызываете услугу без подписки на ответ. Подписка полный список типов mime openOffice см .: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html

Загрузка файла через ajax - всегда болезненный процесс, и, на мой взгляд, лучше всего позволить серверу и браузеру выполнять эту работу по согласованию типа содержимого.

Думаю, лучше всего иметь

 

, чтобы это сделать. Для этого даже не требуется открывать новые окна и тому подобное.

Контроллер MVC, как в вашем примере, может быть таким, как показано ниже:

[HttpGet ("[действие]")]
общедоступная асинхронная задача  DownloadFile ()
{
    // ...
    вернуть файл (dataStream.ToArray (), «текст / простой», «myblob.txt»);
}

Это для людей, которые ищут, как это сделать с помощью HttpClient и файловой заставки:

  1. Установить файловую заставку

npm install file-saver --save

npm install @ types / file-saver --save

Класс обслуживания API:

экспорт () {
    вернуть this.http.get (this.download_endpoint,
        {responseType: 'blob'});
}

Компонент:

импорт {saveAs} из файловой заставки;
exportPdf () {
    this.api_service.export (). subscribe (data => saveAs (data, `pdf report.pdf`));
}

Попробуйте это!

1 - Установить зависимости для отображения всплывающего окна сохранения / открытия файла

npm install file-saver --save
npm install -D @types/file-saver

2- Создать сервис с этой функцией для получения данных

downloadFile (id): Observable  {
    let options = new RequestOptions ({responseType: ResponseContentType.Blob});
    вернуть this.http.get (this._baseUrl + '/' + id, параметры)
        .map (res => res.blob ())
        .catch (this.handleError)
}

3- В компоненте проанализируйте blob с помощью file-saver

import {saveAs as importSaveAs} из "файловой заставки";

  this.myService.downloadFile (this.id) .subscribe (blob => {
            importSaveAs (blob, this.fileName);
        }
    )

У меня работает!

Я использую Angular 4 с объектом 4.3 httpClient. Я изменил ответ, который нашел в техническом блоге Js, который создает объект ссылки, использует его для загрузки, а затем уничтожает его.

Клиент:

doDownload (id: number, contentType: string) {
    вернуть this.http
        .get (this.downloadUrl + id.toString (), {заголовки: новые HttpHeaders (). append ('Content-Type', contentType), responseType: 'blob', обратите внимание: 'body'})
}

downloadFile (id: number, contentType: string, filename: string) {

    вернуть this.doDownload (id, contentType) .subscribe (
        res => {
            var url = window.URL.createObjectURL (res);
            var a = document.createElement ('a');
            document.body.appendChild (а);
            a.setAttribute ('стиль', 'отображение: нет');
            a.href = url;
            a.download = имя файла;
            a.click ();
            window.URL.revokeObjectURL (url);
            a.remove (); // удаляем элемент
        }, error => {
            console.log ('ошибка загрузки:', JSON.stringify (ошибка));
        }, () => {
            console.log ('Загрузка файла завершена.')
        });

}

Значение this.downloadUrl ранее указывало на api. Я использую это для загрузки вложений, поэтому я знаю идентификатор, contentType и имя файла: Я использую API MVC для возврата файла:

 [ResponseCache (Location = ResponseCacheLocation.None, NoStore = true)]
    общедоступный FileContentResult GetAttachment (Int32 attachmentID)
    {
        Вложение AT = filerep.GetAttachment (attachmentID);
        если (AT! = ноль)
        {
            вернуть новый FileContentResult (AT.FileBytes, AT.ContentType);
        }
        еще
        {
            return null;
        }
    }

Класс присоединения выглядит так:

 Вложение открытого класса
{
    общедоступный Int32 AttachmentID {получить; набор; }
    общедоступная строка FileName {получить; набор; }
    общественный байт [] FileBytes {получить; набор; }
    общедоступная строка ContentType {получить; набор; }
}

Репозиторий filerep возвращает файл из базы данных.

Надеюсь, это кому-то поможет :)

Для более новых угловых версий:

npm install file-saver --save
npm install @types/file-saver --save


import {saveAs} from 'file-saver';

this.http.get('endpoint/', {responseType: "blob", headers: {'Accept': 'application/pdf'}})
  .subscribe(blob => {
    saveAs(blob, 'download.pdf');
  });

Для тех, кто использует Redux Pattern

Я добавил в файл-заставку как @Hector Cuevas, названный в его ответе. Используя Angular2 v. 2.3.1, мне не нужно было добавлять @ types / file-saver.

Следующий пример - загрузка журнала в формате PDF.

Действия журнала

общедоступный статический DOWNLOAD_JOURNALS = '[Журналы] Загрузить как PDF';
public downloadJournals (referenceId: string): Action {
 возвращение {
   тип: JournalActions.DOWNLOAD_JOURNALS,
   полезная нагрузка: {referenceId: referenceId}
 };
}

общедоступный статический DOWNLOAD_JOURNALS_SUCCESS = '[Журналы] успешно загружены в формате PDF »;
public downloadJournalsSuccess (blob: Blob): Action {
 возвращение {
   тип: JournalActions.DOWNLOAD_JOURNALS_SUCCESS,
   полезная нагрузка: {blob: blob}
 };
}

Журнал эффектов

@ Effect () скачать $ = this.actions $
    .ofType (JournalActions.DOWNLOAD_JOURNALS)
    .switchMap (({полезная нагрузка}) =>
        this._journalApiService.downloadJournal (payload.referenceId)
        .map ((blob) => this._actions.downloadJournalsSuccess (blob))
        .catch ((err) => handleError (err, this._actions.downloadJournalsFail (err)))
    );

@Effect () downloadJournalSuccess $ = this.actions $
    .ofType (JournalActions.DOWNLOAD_JOURNALS_SUCCESS)
    .map (({payload}) => saveBlobAs (payload.blob, 'journal.pdf'))

Журнал службы

общедоступный журнал загрузок (referenceId: string): Observable  {
    const url = `$ {this._config.momentumApi} / api / journals / $ {referenceId} / download`;
    вернуть this._http.getBlob (url);
}

Служба HTTP

public getBlob = (url: string): Observable  => {
    return this.request ({
        метод: RequestMethod.Get,
        url: url,
        responseType: ResponseContentType.Blob
    });
};

Редуктор журнала Хотя это устанавливает только правильные состояния, используемые в нашем приложении, я все же хотел добавить его, чтобы показать полный шаблон.

case JournalActions.DOWNLOAD_JOURNALS: {
  return Object.assign ({}, состояние,  {загрузка: истина, hasValidationErrors: ложь, ошибки: []});
}

case JournalActions.DOWNLOAD_JOURNALS_SUCCESS: {
  return Object.assign ({}, состояние,  {загрузка: ложь, hasValidationErrors: ложь, ошибки: []});
}

Надеюсь, это поможет.

2022 WebDevInsider