guide blob streaming - devonfw/devon4ng GitHub Wiki
This sample demonstrates how to upload a file to a server. For this, we will need to use an Angular form. In this case we have chosen a simple template-driven form, as the goal of this sample is just to show the process to upload a file. You can learn more about Forms in Angular in the official documentation.
Note
|
The back-end implementation for this sample is located here: devon4j-blob-streaming |
FormData
is an object where you can store key-value pairs that allows you to send through XMLHttpRequest
. You can create a FormData
object as simply as:
....
const formData = new FormData();
formData.append('key', value);
....
I assume you already have your angular application running, if not, you can have a look to our AngularBasicPWA
sample
guide-angular-pwa
We are going to use Angular Material components, so it is necessary to install the dependency with the following command:
npm install --save @angular/material @angular/cdk @angular/animations
These are the component I am going to use for our sample, are material HTML components. For use the template-driven form you do not need to import any component. I am going to create a module called Core where I place the needed imports. After that, I will import Core module on my main App module, and I be able to use these components in any part of my application.
....
@NgModule({
declarations: [],
imports: [CommonModule],
exports: [
MatButtonModule,
MatFormFieldModule,
MatInputModule,
FormsModule,
MatProgressBarModule,
],
})
export class CoreModule {}
....
FormsModule
Will allow us data binding through html and component.
The next step will be to create a component to place the uploading component:
ng generate component uploader
So this will be our project structure so far:
Then, in the app.component.html we need to add the selector for our new component, so it will be represented there. We are not going to create any route for this sample. We can also modify the values for the toolbar.
....
<div class="toolbar" role="banner">
<img
width="40"
alt="Angular Logo"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="
/>
<span>File uploader</span>
</div>
<app-uploader></app-uploader>
<router-outlet></router-outlet>
....
Now, our new component uploader
will be loaded in the root page. Let’s add some code to it.
I will begin editing the html file. First thing we need is an input component, which will allow us to select the file to upload. Furthermore, I added a button which will be the responsible of calling the upload file window. Apart from this, there is also two labels and a progress bar. Labels will give feedback about file upload request, both with an if
clause with uploadSuccess
and uploadFail
global variables that will be in uploader.component.ts
. The progress bar will show the progress of the file being uploaded.
....
<div class="upload">
<div>
<button mat-raised-button (click)="upload()">Upload file</button>
</div>
<label mat-label *ngIf="uploadSuccess"
>The file was upload succesfully!</label
>
<label mat-label *ngIf="uploadFail"
>There was an error uploading the file</label
>
<input
type="file"
#fileUpload
name="fileUpload"
accept="*"
style="display: none"
/>
</div>
<mat-progress-bar
*ngIf="fileInProgress"
[value]="fileProgress"
></mat-progress-bar>
</div>
....
The button will call the upload() method in our uploader.component.ts
, and as we can see, I assigned an identifier for the input, #fileUpload
, so we can reference it from uploader.component.ts
. It accepts any file, and the display none style is because it will be called when we click the button, so it is no necessary to be present in the view.
Our html view should look something similar to this:
Let’s start in our .ts file. In order to interact with the input #fileUpload
, it is necessary to declare it like this:
....
@ViewChild('fileUpload') fileUpload: ElementRef;
constructor() {}
....
And then, the upload() method that the button in html is calling:
....
upload(): void {
this.fileUpload.nativeElement.click();
this.fileUpload.nativeElement.onchange = () => {
const file = this.fileUpload.nativeElement.files[0];
this.uploadFile(file);
};
}
....
The click method at first line will open the file explorer in order to select the desired file to upload, and on change method will be called when a new file is selected, so a change is detected. Then, uploadFile(…)
method will be called.
Before explain this uploadFile(…)
method, there is something still missing, a service to communicate with back-end through HTTP.
I am going to place the service in a service folder inside our uploader
component folder.
Execute the following command ng generate service data
and paste the following code
....
export class DataService {
SERVER_URL = 'http://localhost:8081/services/rest/binary/v1/';
constructor(private httpClient: HttpClient) {}
uploadFile(formdData: FormData): Observable<HttpEvent<BinaryObject>> {
const headers = new HttpHeaders({
'Content-Type': 'multipart/form-data',
});
return this.httpClient.post<BinaryObject>(
this.SERVER_URL + 'binaryobject',
formdData,
{
headers,
reportProgress: true,
observe: 'events',
}
);
}
}
....
We have declared the URL as a global variable. Also is necessary to set the content-type as multipart/form-data in the headers sections, that will be the body of the request. There is also two options to talk about:
-
reportProgress
: to have a feedback about the file upload so we can show percentage on the view. -
observe: ' events' in order to receive this type of events information.
In uploader.component.ts
is missing uploadFile(…)
method.
....
uploadFile(file: File): void {
const formDataBody = this.getFormData(file);
this.dataService.uploadFile(formDataBody).subscribe(
(event) => {
if (event.type === HttpEventType.UploadProgress) {
this.fileProgress = Math.round((100 * event.loaded) / event.total);
} else if (event instanceof HttpResponse) {
this.fileInProgress = false;
this.uploadSuccess = true;
}
},
(err) => {
console.log('Could not upload the file!');
this.uploadFail = true;
}
);
}
....
Notice that whether we have a correct response, or an error response, we set the variable this.uploadSuccess
or this.uploadFail
to show the labels in the html giving feedback.
Once we call the service to do the HTTP request, we expect two types of response(three if we count the error), the first one is the progress of the upload, and will update the progress bar through this.fileProgress
variable. The second one is a response when the request is finished.
That is why the type of the response is checked between HttpEventType
or HttpResponse
.
Now, if you have your back-end running, you should be able to upload a file, and check in DB that all the process worked fine.
Note
|
Download method is not implemented yet. |