在 Angular 中上传文件?
Posted
技术标签:
【中文标题】在 Angular 中上传文件?【英文标题】:File Upload In Angular? 【发布时间】:2017-03-06 00:22:25 【问题描述】:我知道这是一个非常普遍的问题,但我无法在 Angular 2 中上传文件。 我试过了
1) http://valor-software.com/ng2-file-upload/ 和
2)http://ng2-uploader.com/home
...但是失败了。有人在 Angular 中上传文件吗?你用了什么方法?怎么做?如果提供任何示例代码或演示链接,将不胜感激。
【问题讨论】:
【参考方案1】:Angular 2 为上传文件提供了良好的支持。不需要第三方库。
<input type="file" (change)="fileChange($event)" placeholder="Upload file" accept=".pdf,.doc,.docx">
fileChange(event)
let fileList: FileList = event.target.files;
if(fileList.length > 0)
let file: File = fileList[0];
let formData:FormData = new FormData();
formData.append('uploadFile', file, file.name);
let headers = new Headers();
/** In Angular 5, including the header Content-Type can invalidate your request */
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = new RequestOptions( headers: headers );
this.http.post(`$this.apiEndPoint`, formData, options)
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log('success'),
error => console.log(error)
)
使用@angular/core": "~2.0.0" 和@angular/http: "~2.0.0"
【讨论】:
它不起作用,至少在我的情况下。 sailsJs 服务器接收空文件数组/对象 它对我有用,除了-我必须在这条线上工作-headers.append('enctype', 'multipart/form-data');
(使用'enctype'替换'Content-Type')。也许这取决于服务器端代码。 (即 api)
如果 Angular 团队会写一些关于这个主题的文档,那就太好了,我在他们的文档中找不到一行。此代码示例已过时,不适用于 v4+。
注意对于某些应用服务器,设置content-type会被拒绝。你需要让它为空:let headers = new Headers();浏览器会帮你整理好。
LMFAO 为这个废话苦苦挣扎了 20 分钟,直到我意识到我根本不需要设置标题。注意使用 .Net webapi 的 angular 4.x.x 的其他人,不要尝试设置标题!感谢您指出这一点@PeterS【参考方案2】:
根据上面的答案,我使用 Angular 5.x 构建了这个
只需致电uploadFile(url, file).subscribe()
即可触发上传
import Injectable from '@angular/core';
import HttpClient, HttpParams, HttpRequest, HttpEvent from '@angular/common/http';
import Observable from "rxjs";
@Injectable()
export class UploadService
constructor(private http: HttpClient)
// file from event.target.files[0]
uploadFile(url: string, file: File): Observable<HttpEvent<any>>
let formData = new FormData();
formData.append('upload', file);
let params = new HttpParams();
const options =
params: params,
reportProgress: true,
;
const req = new HttpRequest('POST', url, formData, options);
return this.http.request(req);
在你的组件中像这样使用它
// At the drag drop area
// (drop)="onDropFile($event)"
onDropFile(event: DragEvent)
event.preventDefault();
this.uploadFile(event.dataTransfer.files);
// At the drag drop area
// (dragover)="onDragOverFile($event)"
onDragOverFile(event)
event.stopPropagation();
event.preventDefault();
// At the file input element
// (change)="selectFile($event)"
selectFile(event)
this.uploadFile(event.target.files);
uploadFile(files: FileList)
if (files.length == 0)
console.log("No file selected!");
return
let file: File = files[0];
this.upload.uploadFile(this.appCfg.baseUrl + "/api/flash/upload", file)
.subscribe(
event =>
if (event.type == HttpEventType.UploadProgress)
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(`File is $percentDone% loaded.`);
else if (event instanceof HttpResponse)
console.log('File is completely loaded!');
,
(err) =>
console.log("Upload Error:", err);
, () =>
console.log("Upload done");
)
【讨论】:
适用于 Angular6。谢谢你。你需要这些库来导入。从'@angular/common/http'导入HttpClient, HttpParams, HttpRequest, HttpEvent, HttpEventType, HttpResponse; 在我的情况下,我使用的是授权承载并添加了这个额外的代码let params = new HttpParams(); let headers = new HttpHeaders( 'Authorization': 'Bearer ' + localStorage.getItem('accessToken'), ); const options = headers: headers, params: params, reportProgress: true, ;
值得注意的是,如果您可以使用类型推断为uploadFile()
提供函数的返回类型,则可以完全省略Observable
和HttpEvent
的导入! this.http.request()
已经返回了 Observable<HttpEvent<>>
的类型,所以如果你给请求调用一个泛型类型(即this.http.request<any>()
那么整个函数就可以使用正确的类型。
html部分是这样的input type="file" (change)="addFiles($event)" style="display: none" #file multiple> <button mat-raised-button color="primary" (click)="selectFile($event)">Upload File </button>
【参考方案3】:
感谢@Eswar。这段代码对我来说非常有效。我想在解决方案中添加某些内容:
我遇到了错误:java.io.IOException: RESTEASY007550: Unable to get boundary for multipart
为了解决这个错误,您应该删除“Content-Type”“multipart/form-data”。它解决了我的问题。
【讨论】:
+1。如果您删除 Content-Type,它会正确生成。例如:multipart/form-data; boundary=---------------------------186035562730765173675680113
。另见***.com/a/29697774/1475331 和github.com/angular/angular/issues/11819。
我收到此错误java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found"
,这与您的类似,但是当我删除 Content-Type
标头时,我从后端得到了 404。我们正在使用 Spring 和 Angular 2。任何帮助表示赞赏。
这应该只是对他的回答的评论吧?
谢谢,但为什么它不能与“Content-type”标头一起使用?【参考方案4】:
由于代码示例有点过时,我想我会分享一个更新的方法,使用 Angular 4.3 和新的(er)HttpClient API,@angular/common/http
export class FileUpload
@ViewChild('selectedFile') selectedFileEl;
uploadFile()
let params = new HttpParams();
let formData = new FormData();
formData.append('upload', this.selectedFileEl.nativeElement.files[0])
const options =
headers: new HttpHeaders().set('Authorization', this.loopBackAuth.accessTokenId),
params: params,
reportProgress: true,
withCredentials: true,
this.http.post('http://localhost:3000/api/FileUploads/fileupload', formData, options)
.subscribe(
data =>
console.log("Subscribe data", data);
,
(err: HttpErrorResponse) =>
console.log(err.message, JSON.parse(err.error).error.message);
)
.add(() => this.uploadBtn.nativeElement.disabled = false);//teardown
【讨论】:
你有这个的 html 吗?我喜欢这是使用 HttpParams。只是想知道你是否在某个地方有一个完整的工作示例。谢谢 这样如何将多个文件一起上传为一个数组?它应该如何附加到表单数据对象? 查看多部分表单数据webdavsystem.com/javaserver/doc/resumable_upload/multipart_post【参考方案5】:在 Angular 2+ 中,将 Content-Type 留空是非常重要。如果您将“Content-Type”设置为“multipart/form-data”,则上传将不起作用!
upload.component.html
<input type="file" (change)="fileChange($event)" name="file" />
upload.component.ts
export class UploadComponent implements OnInit
constructor(public http: Http)
fileChange(event): void
const fileList: FileList = event.target.files;
if (fileList.length > 0)
const file = fileList[0];
const formData = new FormData();
formData.append('file', file, file.name);
const headers = new Headers();
// It is very important to leave the Content-Type empty
// do not use headers.append('Content-Type', 'multipart/form-data');
headers.append('Authorization', 'Bearer ' + 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9....');
const options = new RequestOptions(headers: headers);
this.http.post('https://api.mysite.com/uploadfile', formData, options)
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log('success'),
error => console.log(error)
);
【讨论】:
【参考方案6】:我已经成功使用了以下工具。我与 primeNg 毫无关系,只是传递我的建议。
http://www.primefaces.org/primeng/#/fileupload
【讨论】:
请告诉我们这是否与 NG2 兼容?? @G1P 它肯定兼容 Angular 4。 primefaces.org/primeng/#/setup【参考方案7】:这个简单的解决方案对我有用:file-upload.component.html
<div>
<input type="file" #fileInput placeholder="Upload file..." />
<button type="button" (click)="upload()">Upload</button>
</div>
然后在组件中直接用XMLHttpRequest进行上传。
import Component, OnInit, ViewChild from '@angular/core';
@Component(
selector: 'app-file-upload',
templateUrl: './file-upload.component.html',
styleUrls: ['./file-upload.component.css']
)
export class FileUploadComponent implements OnInit
@ViewChild('fileInput') fileInput;
constructor()
ngOnInit()
private upload()
const fileBrowser = this.fileInput.nativeElement;
if (fileBrowser.files && fileBrowser.files[0])
const formData = new FormData();
formData.append('files', fileBrowser.files[0]);
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/Data/UploadFiles', true);
xhr.onload = function ()
if (this['status'] === 200)
const responseText = this['responseText'];
const files = JSON.parse(responseText);
//todo: emit event
else
//todo: error handling
;
xhr.send(formData);
如果您使用的是 dotnet core,则参数名称必须与 from 字段名称匹配。在这种情况下的文件:
[HttpPost("[action]")]
public async Task<IList<FileDto>> UploadFiles(List<IFormFile> files)
return await _binaryService.UploadFilesAsync(files);
这个答案抄袭http://blog.teamtreehouse.com/uploading-files-ajax
编辑: 上传后,您必须清除文件上传,以便用户可以选择新文件。而不是使用 XMLHttpRequest,也许使用 fetch 更好:
private addFileInput()
const fileInputParentNative = this.fileInputParent.nativeElement;
const oldFileInput = fileInputParentNative.querySelector('input');
const newFileInput = document.createElement('input');
newFileInput.type = 'file';
newFileInput.multiple = true;
newFileInput.name = 'fileInput';
const uploadfiles = this.uploadFiles.bind(this);
newFileInput.onchange = uploadfiles;
oldFileInput.parentNode.replaceChild(newFileInput, oldFileInput);
private uploadFiles()
this.onUploadStarted.emit();
const fileInputParentNative = this.fileInputParent.nativeElement;
const fileInput = fileInputParentNative.querySelector('input');
if (fileInput.files && fileInput.files.length > 0)
const formData = new FormData();
for (let i = 0; i < fileInput.files.length; i++)
formData.append('files', fileInput.files[i]);
const onUploaded = this.onUploaded;
const onError = this.onError;
const addFileInput = this.addFileInput.bind(this);
fetch('/api/Data/UploadFiles',
credentials: 'include',
method: 'POST',
body: formData,
).then((response: any) =>
if (response.status !== 200)
const error = `An error occured. Status: $response.status`;
throw new Error(error);
return response.json();
).then(files =>
onUploaded.emit(files);
addFileInput();
).catch((error) =>
onError.emit(error);
);
https://github.com/yonexbat/cran/blob/master/cranangularclient/src/app/file-upload/file-upload.component.ts
【讨论】:
【参考方案8】:This is useful tutorial,如何使用 ng2-file-upload 和 WITHOUT ng2-file-upload 上传文件。
对我来说很有帮助。
目前,教程包含几个错误:
1- 客户端应该与服务器具有相同的上传 url,
所以在app.component.ts
换行
const URL = 'http://localhost:8000/api/upload';
到
const URL = 'http://localhost:3000';
2- 服务器发送响应为'text/html',所以在app.component.ts
改变
.post(URL, formData).map((res:Response) => res.json()).subscribe(
//map the success function and alert the response
(success) =>
alert(success._body);
,
(error) => alert(error))
到
.post(URL, formData)
.subscribe((success) => alert('success'), (error) => alert(error));
【讨论】:
【参考方案9】:上传带有表单域的图片
SaveFileWithData(article: ArticleModel,picture:File): Observable<ArticleModel>
let headers = new Headers();
// headers.append('Content-Type', 'multipart/form-data');
// headers.append('Accept', 'application/json');
let requestoptions = new RequestOptions(
method: RequestMethod.Post,
headers:headers
);
let formData: FormData = new FormData();
if (picture != null || picture != undefined)
formData.append('files', picture, picture.name);
formData.append("article",JSON.stringify(article));
return this.http.post("url",formData,requestoptions)
.map((response: Response) => response.json() as ArticleModel);
就我而言,我需要 C# 中的 .NET Web Api
// POST: api/Articles
[ResponseType(typeof(Article))]
public async Task<IHttpActionResult> PostArticle()
Article article = null;
try
HttpPostedFile postedFile = null;
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count == 1)
postedFile = httpRequest.Files[0];
var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
postedFile.SaveAs(filePath);
var json = httpRequest.Form["article"];
article = JsonConvert.DeserializeObject <Article>(json);
if (!ModelState.IsValid)
return BadRequest(ModelState);
article.CreatedDate = DateTime.Now;
article.CreatedBy = "Abbas";
db.articles.Add(article);
await db.SaveChangesAsync();
catch (Exception ex)
int a = 0;
return CreatedAtRoute("DefaultApi", new id = article.Id , article);
【讨论】:
【参考方案10】:今天我将ng2-file-upload包集成到我的angular 6应用程序中,非常简单,请找到下面的高级代码。
导入 ng2-file-upload 模块
app.module.ts
import FileUploadModule from 'ng2-file-upload';
------
------
imports: [ FileUploadModule ],
------
------
组件ts文件导入FileUploader
app.component.ts
import FileUploader, FileLikeObject from 'ng2-file-upload';
------
------
const URL = 'http://localhost:3000/fileupload/';
------
------
public uploader: FileUploader = new FileUploader(
url: URL,
disableMultipart : false,
autoUpload: true,
method: 'post',
itemAlias: 'attachment'
);
public onFileSelected(event: EventEmitter<File[]>)
const file: File = event[0];
console.log(file);
------
------
组件 HTML 添加文件标签
app.component.html
<input type="file" #fileInput ng2FileSelect [uploader]="uploader" (onFileSelected)="onFileSelected($event)" />
在线工作 stackblitz 链接: https://ng2-file-upload-example.stackblitz.io
Stackblitz 代码示例: https://stackblitz.com/edit/ng2-file-upload-example
官方文档链接https://valor-software.com/ng2-file-upload/
【讨论】:
【参考方案11】:尽量不要设置options
参数
this.http.post($this.apiEndPoint, formData)
并确保您没有在 Http 工厂中设置 globalHeaders
。
【讨论】:
【参考方案12】:jspdf 和 Angular 8
我生成了一个 pdf 并想通过 POST 请求上传 pdf,我就是这样做的(为了清楚起见,我删除了一些代码和服务层)
import * as jsPDF from 'jspdf';
import HttpClient from '@angular/common/http';
constructor(private http: HttpClient)
upload()
const pdf = new jsPDF()
const blob = pdf.output('blob')
const formData = new FormData()
formData.append('file', blob)
this.http.post('http://your-hostname/api/upload', formData).subscribe()
【讨论】:
【参考方案13】:我已使用参考上传文件。这种方式上传文件不需要打包。
//要写入.ts文件的代码
@ViewChild("fileInput") fileInput;
addFile(): void
let fi = this.fileInput.nativeElement;
if (fi.files && fi.files[0])
let fileToUpload = fi.files[0];
this.admin.addQuestionApi(fileToUpload)
.subscribe(
success =>
this.loading = false;
this.flashMessagesService.show('Uploaded successfully',
classes: ['alert', 'alert-success'],
timeout: 1000,
);
,
error =>
this.loading = false;
if(error.statusCode==401) this.router.navigate(['']);
else
this.flashMessagesService.show(error.message,
classes: ['alert', 'alert-danger'],
timeout: 1000,
);
);
//要写入service.ts文件的代码
addQuestionApi(fileToUpload: any)
var headers = this.getHeadersForMultipart();
let input = new FormData();
input.append("file", fileToUpload);
return this.http.post(this.baseUrl+'addQuestions', input, headers:headers)
.map(response => response.json())
.catch(this.errorHandler);
// 用html编写的代码
<input type="file" #fileInput>
【讨论】:
【参考方案14】:在最简单的形式中,以下代码适用于 Angular 6/7
this.http.post("http://destinationurl.com/endpoint", fileFormData)
.subscribe(response =>
//handle response
, err =>
//handle error
);
这里是complete implementation
【讨论】:
以上是关于在 Angular 中上传文件?的主要内容,如果未能解决你的问题,请参考以下文章