Как мне скачать файл с Angular2
У меня есть приложение WebApi / MVC, для которого я разрабатываю клиент angular2 (вместо MVC). У меня возникли проблемы с пониманием того, как Angular сохраняет файл.
Запрос в порядке (отлично работает с MVC, и мы можем регистрировать полученные данные), но я не могу понять, как сохранить загруженные данные (я в основном следую той же логике, что и в этом посте). Я уверен, что это глупо просто, но пока я просто не понимаю этого.
Код функции компонента приведен ниже. Я пробовал разные альтернативы, путь blob должен быть таким, насколько я понял, но функции нет createObjectURL
в URL
, Я даже не могу найти определение URL
в окне, но, видимо, он существует. Если я использую FileSaver.js
Модуль я получаю ту же ошибку. Поэтому я думаю, что это то, что изменилось недавно или еще не реализовано. Как я могу запустить сохранение файла в A2?
downloadfile(type: string){
let thefile = {};
this.pservice.downloadfile(this.rundata.name, type)
.subscribe(data => thefile = new Blob([data], { type: "application/octet-stream" }), //console.log(data),
error => console.log("Error downloading the file."),
() => console.log('Completed file download.'));
let url = window.URL.createObjectURL(thefile);
window.open(url);
}
Для полноты службы, которая извлекает данные, ниже, но единственное, что она делает, - это отправляет запрос и передает данные без сопоставления, если это удается:
downloadfile(runname: string, type: string){
return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
.catch(this.logAndPassOn);
}
32 ответа
Проблема в том, что наблюдаемое запускается в другом контексте, поэтому при попытке создать переменную URL у вас появляется пустой объект, а не нужный вам большой двоичный объект.
Один из многих способов, которые существуют для решения этой проблемы, заключается в следующем:
this._reportService.getReport().subscribe(data => this.downloadFile(data)),//console.log(data),
error => console.log('Error downloading the file.'),
() => console.info('OK');
Когда запрос будет готов, он вызовет функцию "downloadFile", которая определяется следующим образом:
downloadFile(data: Response) {
const blob = new Blob([data], { type: 'text/csv' });
const url= window.URL.createObjectURL(blob);
window.open(url);
}
BLOB-объект создан идеально, поэтому URL-адрес var, если не открывается новое окно, проверьте, что вы уже импортировали 'rxjs/Rx';
import 'rxjs/Rx' ;
надеюсь, это поможет вам.
Попробуй это!
1 - Установить зависимости для показа всплывающего окна сохранения / открытия файла
npm install file-saver --save
npm install @types/file-saver --save
2- Создайте сервис с этой функцией для получения данных.
downloadFile(id): Observable<Blob> {
let options = new RequestOptions({responseType: ResponseContentType.Blob });
return this.http.get(this._baseUrl + '/' + id, options)
.map(res => res.blob())
.catch(this.handleError)
}
3- В компоненте анализировать BLOB-объект с помощью "файл-заставка"
import {saveAs as importedSaveAs} from "file-saver";
this.myService.downloadFile(this.id).subscribe(blob => {
importedSaveAs(blob, this.fileName);
}
)
Это работает для меня!
Если вам не нужно добавлять заголовки в запросе, скачать файл в Angular2 вы можете просто:
window.location.href='http://example.com/myuri/report?param=x';
в вашем компоненте.
Это для людей, которые ищут, как это сделать, используя HttpClient и File-Saver:
- Установить файл-заставку
npm установить файл-заставку --save
npm install @ types / file-saver --save
Класс обслуживания API:
export() {
return this.http.get(this.download_endpoint,
{responseType: 'blob'});
}
Составная часть:
import { saveAs } from 'file-saver';
exportPdf() {
this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`));
}
Как насчет этого?
this.http.get(targetUrl,{responseType:ResponseContentType.Blob})
.catch((err)=>{return [do yourself]})
.subscribe((res:Response)=>{
var a = document.createElement("a");
a.href = URL.createObjectURL(res.blob());
a.download = fileName;
// start download
a.click();
})
Я мог сделать с этим.
Не нужно дополнительного пакета.
Для более новых угловых версий:
npm install file-saver --save
npm install @types/file-saver --save
import {saveAs} from 'file-saver/FileSaver';
this.http.get('endpoint/', {responseType: "blob", headers: {'Accept': 'application/pdf'}})
.subscribe(blob => {
saveAs(blob, 'download.pdf');
});
Как упомянул Алехандро Корредор, это простая ошибка. subscribe
выполняется асинхронно и open
должны быть помещены в этот контекст, чтобы данные заканчивали загрузку, когда мы запускаем загрузку.
Тем не менее, есть два способа сделать это. Как рекомендуют документы, служба заботится о получении и сопоставлении данных:
//On the service:
downloadfile(runname: string, type: string){
var headers = new Headers();
headers.append('responseType', 'arraybuffer');
return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
.map(res => new Blob([res],{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }))
.catch(this.logAndPassOn);
}
Затем на компонент мы просто подписываемся и имеем дело с сопоставленными данными. Есть две возможности. Первый, как предлагается в оригинальном посте, но нуждается в небольшой коррекции, как отметил Алехандро:
//On the component
downloadfile(type: string){
this.pservice.downloadfile(this.rundata.name, type)
.subscribe(data => window.open(window.URL.createObjectURL(data)),
error => console.log("Error downloading the file."),
() => console.log('Completed file download.'));
}
Второй способ - использовать FileReader. Логика та же, но мы можем явно ждать, пока FileReader загрузит данные, избегая вложения и решая проблему асинхронности.
//On the component using FileReader
downloadfile(type: string){
var reader = new FileReader();
this.pservice.downloadfile(this.rundata.name, type)
.subscribe(res => reader.readAsDataURL(res),
error => console.log("Error downloading the file."),
() => console.log('Completed file download.'));
reader.onloadend = function (e) {
window.open(reader.result, 'Excel', 'width=20,height=10,toolbar=0,menubar=0,scrollbars=no');
}
}
Примечание. Я пытаюсь загрузить файл Excel, и, хотя загрузка запускается (поэтому это отвечает на вопрос), файл поврежден. Смотрите ответ на этот пост, чтобы избежать повреждения файла.
Скачайте *.zip решение для angular 2.4.x: вы должны импортировать ResponseContentType из '@angular/http' и изменить responseType на ResponseContentType.ArrayBuffer (по умолчанию это ResponseContentType.Json)
getZip(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> {
let headers = this.setHeaders({
'Content-Type': 'application/zip',
'Accept': 'application/zip'
});
return this.http.get(`${environment.apiUrl}${path}`, {
headers: headers,
search: params,
responseType: ResponseContentType.ArrayBuffer //magic
})
.catch(this.formatErrors)
.map((res:Response) => res['_body']);
}
Я использую Angular 4 с 4,3 httpClient объект. Я изменил ответ, который нашел в техническом блоге Js, который создает объект ссылки, использует его для загрузки, а затем уничтожает его.
Клиент:
doDownload(id: number, contentType: string) {
return this.http
.get(this.downloadUrl + id.toString(), { headers: new HttpHeaders().append('Content-Type', contentType), responseType: 'blob', observe: 'body' })
}
downloadFile(id: number, contentType: string, filename:string) {
return this.doDownload(id, contentType).subscribe(
res => {
var url = window.URL.createObjectURL(res);
var a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('style', 'display: none');
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
a.remove(); // remove the element
}, error => {
console.log('download error:', JSON.stringify(error));
}, () => {
console.log('Completed file download.')
});
}
Значение this.downloadUrl было установлено ранее, чтобы указывать на API. Я использую это для загрузки вложений, поэтому я знаю идентификатор, contentType и имя файла: я использую API MVC для возврата файла:
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public FileContentResult GetAttachment(Int32 attachmentID)
{
Attachment AT = filerep.GetAttachment(attachmentID);
if (AT != null)
{
return new FileContentResult(AT.FileBytes, AT.ContentType);
}
else
{
return null;
}
}
Класс вложения выглядит так:
public class Attachment
{
public Int32 AttachmentID { get; set; }
public string FileName { get; set; }
public byte[] FileBytes { get; set; }
public string ContentType { get; set; }
}
Репозиторий filerep возвращает файл из базы данных.
Надеюсь, это поможет кому-то:)
Загрузка файла через ajax всегда является болезненным процессом, и, на мой взгляд, лучше всего позволить серверу и браузеру выполнять эту работу по согласованию типа контента.
Я думаю, что лучше всего иметь
<a href="api/sample/download"></a>
сделать это. Это даже не требует открытия новых окон и тому подобного.
Контроллер MVC, как в вашем примере, может быть таким, как показано ниже:
[HttpGet("[action]")]
public async Task<FileContentResult> DownloadFile()
{
// ...
return File(dataStream.ToArray(), "text/plain", "myblob.txt");
}
Будет лучше, если вы попытаетесь вызвать новый метод внутри себя. subscribe
this._reportService.getReport()
.subscribe((data: any) => {
this.downloadFile(data);
},
(error: any) => onsole.log(error),
() => console.log('Complete')
);
внутри downloadFile(data)
функция, которую мы должны сделать block, link, href and file name
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);
}
}
Что ж, я написал фрагмент кода, вдохновленный многими из приведенных выше ответов, который должен легко работать в большинстве сценариев, когда сервер отправляет файл с заголовком размещения содержимого без каких-либо сторонних установок, кроме rxjs и angular.
Во-первых, как вызвать код из вашего файла компонента
this.httpclient.get(
`${myBackend}`,
{
observe: 'response',
responseType: 'blob'
}
).pipe(first())
.subscribe(response => SaveFileResponse(response, 'Custom File Name.extension'));
Как видите, это в основном средний бэкэнд-вызов из angular с двумя изменениями.
- Я наблюдаю реакцию вместо тела
- Я прямо говорю о том, что ответ - это капля
Как только файл загружен с сервера, я, в принципе, делегирую всю задачу сохранения файла вспомогательной функции, которую я сохраняю в отдельном файле, и импортирую в любой компонент, который мне нужно
export const SaveFileResponse =
(response: HttpResponse<Blob>,
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! Мы можем использовать любое имя, предоставляемое сервером, без необходимости явно указывать его в клиенте, или перезаписывать имя файла, предоставленное сервером (как в этом примере). Кроме того, при необходимости можно легко изменить алгоритм извлечения имени файла из содержимого-расположения в соответствии со своими потребностями, и все остальное останется неизменным - в случае ошибки во время такого извлечения он просто передаст 'null' как имя файла.
Как уже указывалось в другом ответе, IE, как всегда, требует особого обращения. Но с появлением хрома Edge через несколько месяцев я бы не стал беспокоиться об этом при создании новых приложений (надеюсь). Существует также вопрос об отзыве URL-адреса, но я не совсем уверен в этом, поэтому, если бы кто-то мог помочь с этим в комментариях, это было бы здорово.
Я поделился решением, которое помогло мне (любое улучшение очень ценится)
К вашим услугам "Псервис":
getMyFileFromBackend(typeName: string): Observable<any>{
let param = new URLSearchParams();
param.set('type', typeName);
// setting 'responseType: 2' tells angular that you are loading an arraybuffer
return this.http.get(http://MYSITE/API/FILEIMPORT, {search: params, responseType: 2})
.map(res => res.text())
.catch((error:any) => Observable.throw(error || 'Server error'));
}
Составная часть:
downloadfile(type: string){
this.pservice.getMyFileFromBackend(typename).subscribe(
res => this.extractData(res),
(error:any) => Observable.throw(error || 'Server error')
);
}
extractData(res: string){
// transforme response to blob
let myBlob: Blob = new Blob([res], {type: 'application/vnd.oasis.opendocument.spreadsheet'}); // replace the type by whatever type is your response
var fileURL = URL.createObjectURL(myBlob);
// Cross your fingers at this point and pray whatever you're used to pray
window.open(fileURL);
}
В компонентной части вы вызываете сервис без подписки на ответ. Подписка на полный список типов пантомимы openOffice приведена по адресу: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html
Для тех, кто использует Redux Pattern
Я добавил в заставку файл @Hector Cuevas, названный в его ответе. Используя Angular2 v. 2.3.1, мне не нужно было добавлять в @types/file-saver.
Следующий пример - загрузить журнал в формате PDF.
Журнал действий
public static DOWNLOAD_JOURNALS = '[Journals] Download as PDF';
public downloadJournals(referenceId: string): Action {
return {
type: JournalActions.DOWNLOAD_JOURNALS,
payload: { referenceId: referenceId }
};
}
public static DOWNLOAD_JOURNALS_SUCCESS = '[Journals] Download as PDF Success';
public downloadJournalsSuccess(blob: Blob): Action {
return {
type: JournalActions.DOWNLOAD_JOURNALS_SUCCESS,
payload: { blob: blob }
};
}
Журнал эффектов
@Effect() download$ = this.actions$
.ofType(JournalActions.DOWNLOAD_JOURNALS)
.switchMap(({payload}) =>
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'))
Журнал службы
public downloadJournal(referenceId: string): Observable<any> {
const url = `${this._config.momentumApi}/api/journals/${referenceId}/download`;
return this._http.getBlob(url);
}
HTTP сервис
public getBlob = (url: string): Observable<any> => {
return this.request({
method: RequestMethod.Get,
url: url,
responseType: ResponseContentType.Blob
});
};
Редуктор журналаХотя это только устанавливает правильные состояния, используемые в нашем приложении, я все же хотел добавить его, чтобы показать полный шаблон.
case JournalActions.DOWNLOAD_JOURNALS: {
return Object.assign({}, state, <IJournalState>{ downloading: true, hasValidationErrors: false, errors: [] });
}
case JournalActions.DOWNLOAD_JOURNALS_SUCCESS: {
return Object.assign({}, state, <IJournalState>{ downloading: false, hasValidationErrors: false, errors: [] });
}
Я надеюсь, что это полезно.
Я нашел ответы, на которые пока не хватало понимания, а также предупреждений. Вы можете и должны следить за несовместимостью с IE10+ (если вам не все равно).
Это полный пример с прикладной частью и служебной частью после. Обратите внимание на то, что мы установили "response" для отслеживания заголовка имени файла. Также обратите внимание, что заголовок Content-Disposition должен быть установлен и предоставлен сервером, иначе текущий Angular HttpClient не передаст его. Для этого я добавил основной код dotnet ниже.
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.");
}
}
Чтобы загрузить и показать файлы PDF, очень похожий фрагмент кода, как показано ниже:
private downloadFile(data: Response): void {
let blob = new Blob([data.blob()], { type: "application/pdf" });
let url = window.URL.createObjectURL(blob);
window.open(url);
}
public showFile(fileEndpointPath: string): void {
let reqOpt: RequestOptions = this.getAcmOptions(); // getAcmOptions is our helper method. Change this line according to request headers you need.
reqOpt.responseType = ResponseContentType.Blob;
this.http
.get(fileEndpointPath, reqOpt)
.subscribe(
data => this.downloadFile(data),
error => alert("Error downloading file!"),
() => console.log("OK!")
);
}
Вот что я сделал в моем случае -
// 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"));
}
На решение ссылаются из - здесь
Следующий код работал у меня
let link = document.createElement('a');
link.href = data.fileurl; //data is object received as response
link.download = data.fileurl.substr(data.fileurl.lastIndexOf('/') + 1);
link.click();
Обновите ответ Гектора, используя файл-заставку и HttpClient для шага 2:
public downloadFile(file: File): Observable<Blob> {
return this.http.get(file.fullPath, {responseType: 'blob'})
}
Привет у меня есть решение для загрузки из Angular 2 без повреждения.... с помощью Spring MVC и Angular 2
1-й - мой тип возвращаемого значения: -ResponseEntity с конца Java... здесь я отправляю массив байтов [] имеет тип возвращаемого значения из контроллера.
2-й - включить файловую заставку в ваше рабочее пространство - на странице указателя как: -
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
3-й компонент ts, напишите этот код:
import {ResponseContentType} from '@angular.core';
let headers = new Headers({ 'Content-Type': 'application/json', 'MyApp-Application' : 'AppName', 'Accept': 'application/pdf' });
let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob });
this.http
.post('/project/test/export',
somevalue,options)
.subscribe(data => {
var mediaType = 'application/vnd.ms-excel';
let blob: Blob = data.blob();
window['saveAs'](blob, 'sample.xls');
});
это даст вам формат файла xls... если вам нужны другие форматы, измените медиатип и имя файла с правильным расширением... спасибо, надеюсь, это поможет....
<a href="my_url" download="myfilename">Download file</a>
my_url должен иметь то же происхождение, иначе он будет перенаправлен в это место
Angular 12 + веб-API ASP.NET 5
Вы можете вернуть объект Blob с сервера и создать тег привязки и установить для свойства href URL-адрес объекта, созданный из Blob. Теперь нажатие на якорь загрузит файл. Вы также можете установить имя файла.
downloadFile(path: string): Observable<any> {
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<IActionResult> 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();
}
Сегодня я столкнулся с тем же случаем, мне пришлось загрузить файл PDF в виде вложения (файл не должен отображаться в браузере, а загружаться вместо него). Чтобы добиться этого, я обнаружил, что мне нужно получить файл в Angular Blob
и в то же время добавить Content-Disposition
Заголовок в ответе.
Это было самое простое, что я мог получить (Angular 7):
Внутри сервиса:
getFile(id: String): Observable<HttpResponse<Blob>> {
return this.http.get(`./file/${id}`, {responseType: 'blob', observe: 'response'});
}
Затем, когда мне нужно загрузить файл в компонент, я могу просто:
fileService.getFile('123').subscribe((file: HttpResponse<Blob>) => window.location.href = file.url);
ОБНОВИТЬ:
Удалены ненужные настройки заголовка из сервиса
Ответы, которые я нашел, либо не работали на Angular 13.1, либо были ненужными сложными (например, принятый пример) без объяснения причин, почему это необходимо. Было бы полезно для постоянно меняющихся экосистем, таких как Angular, требовать прикрепления номера версии.
Мини-фрагмент, предоставленный пользователем @Aleksandar Angelov, обходит систему сеансов, поэтому необходима ненужная авторизация.
Получив его ответ, я придумал следующий код:
downloadConfiguration(url: string, filename: string) {
this.http.get(url, {responseType: 'blob'})
.subscribe(data => {
// console.log("data", data);
var url = window.URL.createObjectURL(data);
let downloadLink = document.createElement('a');
downloadLink.href = url
downloadLink.setAttribute('download', filename);
downloadLink.click();
});
}
Создайте временный тег привязки, затем щелкните его программно с помощью Javascript.
Проще говоря url
как href
как ниже.
<a href="my_url">Download File</a>
Хотя вопрос старый, ни один из ответов не является жизнеспособным. Насколько я видел, все файлы сначала загружаются в память, а потом сохраняются. Таким образом мы:
- Вызывают отставание, для которого необходимо реализовать пользовательскую загрузку.
- Загрузите файл в память, что означает, что для больших файлов браузер будет аварийно завершать работу.
- Не используйте встроенную функцию загрузки браузера.
Передняя часть достаточно проста (Angular 12):
downloadFile(url: string, fileName: string): void {
const downloadLink = document.createElement('a');
downloadLink.download = fileName;
downloadLink.href = url;
downloadLink.click();
}
На бэкенде (.NET 6) нам нужно работать с потоками и писать в тело ответа:
public void Get(string fileId)
{
var fileName = fileService.GetFileName(fileId);
var fileContentType = fileService.GetFileContentType(fileId);
this.Response.Headers.Add(HeaderNames.ContentType, fileContentType);
this.Response.Headers.Add(HeaderNames.ContentDisposition, $"attachment; filename=\"{fileName}\"");
fileService.GetFile(Response.Body, fileId);
}
Тип и имя содержимого файла можно получить либо из БД (если вы сохраните там информацию о файле), либо из файловой системы. Тип содержимого анализируется из расширения.
Я пишу в поток так:
public void GetFile(Stream writeStream, string fileId)
{
var file = GetFileInfo(fileId);
try
{
var fileStream = File.OpenRead(file.FullName);
byte[] buffer = new byte[32768];
int read;
while ((read = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
writeStream.Write(buffer, 0, read);
}
writeStream.Flush();
}
catch (Exception e)
{
throw new CustomException($"Error occured while reading the file. Inner Exception Message: ({e.Message}) Stack Trace: ({e.StackTrace})", ErrorCode.FileReadFailure, e);
}
}
Имейте в виду, что я упростил свою реализацию для презентационных целей, поэтому она не тестировалась.
Вы также можете скачать файл прямо из вашего шаблона, где вы используете атрибут загрузки и [attr.href]
Вы можете предоставить значение свойства из компонента. Это простое решение должно работать в большинстве браузеров.
<a download [attr.href]="yourDownloadLink"></a>
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, вы можете сделать это следующим образом:
downloadfile(runname: string, type: string): string {
return window.location.href = `${this.files_api + this.title +"/"+ runname + "/?file="+ type}`;
}
в сервисе, который получает параметры