本文共 13601 字,大约阅读时间需要 45 分钟。
hapi
In this article, we will talk about how to handle file uploads with Angular. We will create an images uploader that allow user to upload single or multiple images file by drag and drop or select file dialog.
在本文中,我们将讨论如何使用Angular处理文件上传。 我们将创建一个图像上传器,允许用户通过拖放或选择文件对话框上传单个或多个图像文件。
We will then upload the selected images and display them accordingly. We will also learn to filter the upload file type, for example, we only allow images, do not allow file type like PDF.
然后,我们将上传选定的图像并进行相应显示。 我们还将学习过滤上传文件的类型,例如,我们只允许图片,不允许像PDF这样的文件类型。
File upload consists of two parts: the UI (front-end) and the API (back-end). We will be using Angular to handle the UI part. We need a backend application to accept the uploaded files. You may follow the backend tutorials or download and run either one of these server side application to handle file upload for your backend:-
文件上传由两部分组成: UI(前端)和API(后端) 。 我们将使用Angular处理UI部分。 我们需要一个后端应用程序来接受上传的文件。 您可以按照后端教程进行操作,也可以下载并运行以下任一服务器端应用程序来处理后端的文件上传:-
We will be using File upload with Hapi.js as our backend throughout this articles. We will also learn the tricks to enable fake upload on the front-end.
在本文中,我们将使用Hapi.js的文件上传作为我们的后端。 我们还将学习在前端启用假冒上传的技巧。
Alright, let's start creating our Angular file upload component.
好了,让我们开始创建Angular文件上传组件。
Notes:-
笔记:-
enctype="multipart/form-data"
is important. To enable file upload, this attribute must be set. Learn more about enctype . 表单属性enctype="multipart/form-data"
很重要。 要启用文件上传,必须设置此属性。 了解有关enctype的更多信息。 <input type="file" />
to accept file upload. The property multiple
indicate it's allow multiple file upload. Remove it for single file upload. 我们有一个文件输入<input type="file" />
来接受文件上传。 属性multiple
表示允许上传多个文件。 将其删除以上传单个文件。 change
event. Whenever the file input change (someone drop or select files), we will trigger the filesChange
function and pass in the control name and selected files $event.target.files
, and then upload to server. 我们将处理文件输入change
事件。 每当文件输入更改(有人删除或选择文件)时,我们将触发filesChange
函数并传入控件名称和所选文件$event.target.files
,然后上载到服务器。 accept="image/*"
. 我们将文件输入限制为仅接受具有属性accept="image/*"
。 #photos
to the file input. This gives us a reference to the file input control. Later, you can see we use the photos
variable in displaying number of files uploading Uploading { { photos.files.length }} files...
. 我们将模板变量#photos
设置为文件输入。 这为我们提供了文件输入控件的参考。 稍后,您可以看到我们使用photos
变量显示了上传的文件数。 Uploading { { photos.files.length }} files...
Now, that's the interesting part. Currently, our component look like this:
现在,这是有趣的部分。 当前,我们的组件如下所示:
We need to transform it to look like this:
我们需要将其转换为如下形式:
Let's style it!
让我们来造型吧!
/* page-file-upload.component.css */.dropbox { outline: 2px dashed grey; /* the dash box */ outline-offset: -10px; background: lightcyan; color: dimgray; padding: 10px 10px; min-height: 200px; /* minimum height */ position: relative; cursor: pointer;}.dropbox:hover { background: lightblue; /* when mouse over to the drop zone, change color */}input[type="file"] { opacity: 0; /* invisible but it's there! */ width: 100%; height: 200px; position: absolute; cursor: pointer;}.dropbox p { font-size: 1.2em; text-align: center; padding: 50px 0;}
With only few lines of css, our component looks prettier now.
只需几行CSS,我们的组件现在看起来更漂亮了。
Notes:-
笔记:-
opacity: 0
style. This doesn't hide the file input, it just make it invisible. 我们通过应用opacity: 0
样式使文件输入不可见。 这不会隐藏文件输入,只会使其不可见。 dropbox
css class. We make it look like a drop file zone surround with dash. 然后,我们设置文件输入父元素dropbox
css类的样式。 我们使它看起来像是一个带有短划线的拖放文件区域。 Let's proceed to code our component class.
让我们继续编写我们的组件类。
// page-file-upload.component.tsimport { Component } from '@angular/core';import { FileUploadService } from './file-upload.service'; // we will create this next!@Component({ selector: 'page-file-upload', templateUrl: './page-file-upload.component.html', styleUrls: ['./page-file-upload.component.css']})export class PageFileUploadComponent { uploadedFiles = []; uploadError; currentStatus: number; uploadFieldName = 'photos'; readonly STATUS_INITIAL = 0; readonly STATUS_SAVING = 1; readonly STATUS_SUCCESS = 2; readonly STATUS_FAILED = 3; constructor(private _svc: FileUploadService) { this.reset(); // set initial state } filesChange(fieldName: string, fileList: FileList) { // handle file changes const formData = new FormData(); if (!fileList.length) return; // append the files to FormData Array .from(Array(fileList.length).keys()) .map(x => { formData.append(fieldName, fileList[x], fileList[x].name); }); // save it this.save(formData); } reset() { this.currentStatus = this.STATUS_INITIAL; this.uploadedFiles = []; this.uploadError = null; } save(formData: FormData) { // upload data to the server this.currentStatus = this.STATUS_SAVING; this._svc.upload(formData) .take(1) .delay(1500) // DEV ONLY: delay 1.5s to see the changes .subscribe(x => { this.uploadedFiles = [].concat(x); this.currentStatus = this.STATUS_SUCCESS; }, err => { this.uploadError = err; this.currentStatus = this.STATUS_FAILED; }) }}
Notes:-
笔记:-
photos
. That's our file input field name. 稍后,我们将调用 API来上传图片,该API接受现场调用photos
。 那就是我们的文件输入字段名称。 filesChange
function. FileList
is an object returned by the files property of the HTML element. It allow us to access the list of files selected with the element. Learn more [here]((). 我们使用filesChange
函数处理文件更改。 FileList
是HTML元素的files属性返回的对象。 它允许我们访问用元素选择的文件的列表。 在此处[( )了解更多信息。 FormData
, and append all our photos
files to it. FormData
interface provides a way to easily construct a set of key/value pairs representing form fields and their values. Learn more . 然后,我们创建一个新的FormData
,并将所有photos
文件附加到该文件。 FormData
接口提供了一种轻松构造一组代表表单字段及其值的键/值对的方法。 了解更多。 save
function will call our file upload service (hang on, we will create the service next!). We also set the status according to the result. save
功能将调用我们的文件上传服务(继续,我们接下来将创建该服务!)。 我们还根据结果设置状态。 // file-upload.service.tsimport { Injectable } from '@angular/core';import { Http, RequestOptionsArgs, Headers } from '@angular/http';@Injectable()export class FileUploadService { baseUrl = 'http://localhost:3001'; // our local Hapi Js API constructor(private _http: Http) { } upload(formData) { const url = `${ this.baseUrl}/photos/upload`; return this._http.post(url, formData) .map(x => x.json()) .map((x: any[]) => x // add a new field url to be used in UI later .map(item => Object .assign({ }, item, { url: `${ this.baseUrl}/images/${ item.id}` })) ); }}
Nothing much, the code is pretty expressive itself. We upload the files, wait for the result, map it accordingly.
没什么,代码本身也很容易表达。 我们上传文件,等待结果,并相应地映射它。
Now wire up your component and service to module, usually app.module.ts
, and run it.
现在,将您的组件和服务连接到模块(通常是app.module.ts
)并运行它。
We can upload the files successfully now. However, there's no indication in UI. Let's update our HTML.
我们现在可以成功上传文件。 但是,UI中没有指示。 让我们更新我们HTML。
...
Notes:-
笔记:-
If you are lazy to start the back-end application (Hapi, Express, etc) to handle file upload. Here is a fake service to replace the file upload service.
如果您懒于启动后端应用程序(Hapi,Express等)来处理文件上传。 这是一项伪造的服务,用来代替文件上传服务。
// file-upload.fake.service.tsimport { Injectable } from '@angular/core';import { Observable } from 'rxjs/Rx';@Injectable()export class FileUploadFakeService { upload(formData: any) { const photos: any[] = formData.getAll('photos'); const promises = photos.map((x: File) => this.getImage(x) .then(img => ({ id: img, originalName: x.name, fileName: x.name, url: img }))); return Observable.fromPromise(Promise.all(promises)); } private getImage(file: File) { return new Promise((resolve, reject) => { const fReader = new FileReader(); const img = document.createElement('img'); fReader.onload = () => { img.src = fReader.result; resolve(this.getBase64Image(img)); } fReader.readAsDataURL(file); }) } private getBase64Image(img) { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const dataURL = canvas.toDataURL('image/png'); return dataURL; }}
Came across this solution in this . Pretty useful. My online is using this service.
在此此解决方案。 很有用。 我的在线正在使用此服务。
Basically, what the code do is read the source, draw it in canvas, and save it as data url with the canvas toDataURL
function. Learn more about canvas .
基本上,代码要做的是读取源代码,将其绘制在画布中,然后使用canvas toDataURL
函数将其另存为数据url。 了解有关画布的更多信息。
If you realize, our fake service has a same public interface as the real file upload service, both has upload
function and return list of files. This is important for the next step, swap the real file upload service with the fake one.
如果您意识到,我们的虚假服务具有与真实文件上传服务相同的公共接口,既具有upload
功能,又具有文件返回列表。 这对下一步很重要,将真实文件上传服务与伪造文件交换服务交换。
First you might think that to use the fake service, you need to register the fake service in module, and import it in our file upload component like how we do usually. However, there's a quicker way, with Angular dependency injection (DI). Let's look at our App module.
首先,您可能会认为要使用伪造的服务,您需要在模块中注册伪造的服务,然后像通常一样将其导入到我们的文件上传组件中。 但是,有了Angular依赖注入(DI),有一种更快的方法。 让我们看看我们的App模块。
// app.module.ts...import { PageFileUploadComponent, FileUploadFakeService, FileUploadService } from './file-upload';@NgModule({ ... providers: [ // FileUploadService, // normally we do this, comment it, we do the below instead { provide: FileUploadService, useClass: FileUploadFakeService }, // we can do this instead ], ...})export class AppModule { }
With this, you don't need to change your component code, stop your backend API, refresh the browser, you should see our app is still working, calling fake service instead.
这样,您无需更改组件代码,停止后端API,刷新浏览器,您应该会看到我们的应用程序仍在运行,而改为调用伪造服务。
In short, Providers: [FileUploadService]
is the short form of Providers: [{ provide: FileUploadService, useClass: FileUploadService }]
. Therefore, as long as we have another class with similar interface, we can swap it easily.
简而言之, Providers: [FileUploadService]
是Providers: [{ provide: FileUploadService, useClass: FileUploadService }]
的简称Providers: [{ provide: FileUploadService, useClass: FileUploadService }]
。 因此,只要我们有另一个具有相似接口的类,我们就可以轻松地对其进行交换。
Angular DI is powerful. We'll leave that for another post.
Angular DI功能强大。 我们将其留在另一篇文章中。
That's it. This is how you can handle file upload without using any 3rd party libraries and plugins in Angular. It isn't that hard right?
而已。 这是您无需使用Angular中的任何第三方库和插件即可处理文件上传的方式。 这不是很难吗?
Happy coding!
编码愉快!
The UI (Front-end)
用户界面(前端)
The API (Back-end)
API(后端)
翻译自:
hapi
转载地址:http://ueuwd.baihongyu.com/