I\'m a beginner with Angular, I want to know how to create Angular 5 File upload part, I\'m trying to find any tutorial or doc, but I don\'t see anything a
Very easy and fastest method is using ng2-file-upload.
Install ng2-file-upload via npm. npm i ng2-file-upload --save
At first import module in your module.
import { FileUploadModule } from 'ng2-file-upload';
Add it to [imports] under @NgModule:
imports: [ ... FileUploadModule, ... ]
Markup:
<input ng2FileSelect type="file" accept=".xml" [uploader]="uploader"/>
In your commponent ts:
import { FileUploader } from 'ng2-file-upload';
...
uploader: FileUploader = new FileUploader({ url: "api/your_upload", removeAfterUpload: false, autoUpload: true });
It`is simplest usage of this. To know all power of this see demo
Here is how I did it to upload the excel files:
Directory structure:
app
|-----uploadcomponent
|-----uploadcomponent.module.ts
|-----uploadcomponent.html
|-----app.module.ts
|-----app.component.ts
|-----app.service.ts
uploadcomponent.html
<div>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input type="file" name="profile" enctype="multipart/form-data" accept=".xlsm,application/msexcel" (change)="onChange($event)" />
<button type="submit">Upload Template</button>
<button id="delete_button" class="delete_button" type="reset"><i class="fa fa-trash"></i></button>
</form>
</div>
uploadcomponent.ts
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { Component, OnInit } from '@angular/core';
....
export class UploadComponent implements OnInit {
form: FormGroup;
constructor(private formBuilder: FormBuilder, private uploadService: AppService) {}
ngOnInit() {
this.form = this.formBuilder.group({
profile: ['']
});
}
onChange(event) {
if (event.target.files.length > 0) {
const file = event.target.files[0];
this.form.get('profile').setValue(file);
console.log(this.form.get('profile').value)
}
}
onSubmit() {
const formData = new FormData();
formData.append('file', this.form.get('profile').value);
this.uploadService.upload(formData).subscribe(
(res) => {
this.response = res;
console.log(res);
},
(err) => {
console.log(err);
});
}
}
app.service.ts
upload(formData) {
const endpoint = this.service_url+'upload/';
const httpOptions = headers: new HttpHeaders({ <<<< Changes are here
'Authorization': 'token xxxxxxx'})
};
return this.http.post(endpoint, formData, httpOptions);
}
In Backend I use DJango REST Framework.
models.py
from __future__ import unicode_literals
from django.db import models
from django.db import connection
from django_mysql.models import JSONField, Model
import uuid
import os
def change_filename(instance, filename):
extension = filename.split('.')[-1]
file_name = os.path.splitext(filename)[0]
uuid_name = uuid.uuid4()
return file_name+"_"+str(uuid_name)+"."+extension
class UploadTemplate (Model):
id = models.AutoField(primary_key=True)
file = models.FileField(blank=False, null=False, upload_to=change_filename)
def __str__(self):
return str(self.file.name)
views.py.
class UploadView(APIView):
serializer_class = UploadSerializer
parser_classes = [MultiPartParser]
def get_queryset(self):
queryset = UploadTemplate.objects.all()
return queryset
def post(self, request, *args, **kwargs):
file_serializer = UploadSerializer(data=request.data)
status = None
message = None
if file_serializer.is_valid():
file_serializer.save()
status = "Success"
message = "Success"
else:
status = "Failure"
message = "Failure!"
content = {'status': status, 'message': message}
return Response(content)
serializers.py.
from uploadtemplate.models import UploadTemplate
from rest_framework import serializers
class UploadSerializer(serializers.ModelSerializer):
class Meta:
model = UploadTemplate
fields = '__all__'
urls.py.
router.register(r'uploadtemplate', uploadtemplateviews.UploadTemplateView,
base_name='UploadTemplate')
urlpatterns = [
....
url(r'upload/', uploadtemplateviews.UploadTemplateView.as_view()),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
MEDIA_URL and MEDIA_ROOT is defined in settings.py of the project.
Thanks!
This way I implement upload file to web API in project.
I share for whom concern.
const formData: FormData = new FormData();
formData.append('Image', image, image.name);
formData.append('ComponentId', componentId);
return this.http.post('/api/dashboard/UploadImage', formData);
Step by step
ASP.NET Web API
[HttpPost]
[Route("api/dashboard/UploadImage")]
public HttpResponseMessage UploadImage()
{
string imageName = null;
var httpRequest = HttpContext.Current.Request;
//Upload Image
var postedFile = httpRequest.Files["Image"];
//Create custom filename
if (postedFile != null)
{
imageName = new String(Path.GetFileNameWithoutExtension(postedFile.FileName).Take(10).ToArray()).Replace(" ", "-");
imageName = imageName + DateTime.Now.ToString("yymmssfff") + Path.GetExtension(postedFile.FileName);
var filePath = HttpContext.Current.Server.MapPath("~/Images/" + imageName);
postedFile.SaveAs(filePath);
}
}
HTML form
<form #imageForm=ngForm (ngSubmit)="OnSubmit(Image)">
<img [src]="imageUrl" class="imgArea">
<div class="image-upload">
<label for="file-input">
<img src="upload.jpg" />
</label>
<input id="file-input" #Image type="file" (change)="handleFileInput($event.target.files)" />
<button type="submit" class="btn-large btn-submit" [disabled]="Image.value=='' || !imageForm.valid"><i
class="material-icons">save</i></button>
</div>
</form>
TS file to use API
OnSubmit(Image) {
this.dashboardService.uploadImage(this.componentId, this.fileToUpload).subscribe(
data => {
console.log('done');
Image.value = null;
this.imageUrl = "/assets/img/logo.png";
}
);
}
Service TS
uploadImage(componentId, image) {
const formData: FormData = new FormData();
formData.append('Image', image, image.name);
formData.append('ComponentId', componentId);
return this.http.post('/api/dashboard/UploadImage', formData);
}
Complete example of File upload using Angular and nodejs(express)
HTML Code
<div class="form-group">
<label for="file">Choose File</label><br/>
<input type="file" id="file" (change)="uploadFile($event.target.files)" multiple>
</div>
TS Component Code
uploadFile(files) {
console.log('files', files)
var formData = new FormData();
for(let i =0; i < files.length; i++){
formData.append("files", files[i], files[i]['name']);
}
this.httpService.httpPost('/fileUpload', formData)
.subscribe((response) => {
console.log('response', response)
},
(error) => {
console.log('error in fileupload', error)
})
}
Node Js code
fileUpload API controller
function start(req, res) {
fileUploadService.fileUpload(req, res)
.then(fileUploadServiceResponse => {
res.status(200).send(fileUploadServiceResponse)
})
.catch(error => {
res.status(400).send(error)
})
}
module.exports.start = start
Upload service using multer
const multer = require('multer') // import library
const moment = require('moment')
const q = require('q')
const _ = require('underscore')
const fs = require('fs')
const dir = './public'
/** Store file on local folder */
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public')
},
filename: function (req, file, cb) {
let date = moment(moment.now()).format('YYYYMMDDHHMMSS')
cb(null, date + '_' + file.originalname.replace(/-/g, '_').replace(/ /g, '_'))
}
})
/** Upload files */
let upload = multer({ storage: storage }).array('files')
/** Exports fileUpload function */
module.exports = {
fileUpload: function (req, res) {
let deferred = q.defer()
/** Create dir if not exist */
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
console.log(`\n\n ${dir} dose not exist, hence created \n\n`)
}
upload(req, res, function (err) {
if (req && (_.isEmpty(req.files))) {
deferred.resolve({ status: 200, message: 'File not attached', data: [] })
} else {
if (err) {
deferred.reject({ status: 400, message: 'error', data: err })
} else {
deferred.resolve({
status: 200,
message: 'File attached',
filename: _.pluck(req.files,
'filename'),
data: req.files
})
}
}
})
return deferred.promise
}
}
Personally I'm doing this using ngx-material-file-input for the front-end, and Firebase for the back-end. More precisely Cloud Storage for Firebase for the back-end combined with Cloud Firestore. Below an example, which limits file to be not larger than 20 MB, and accepts only certain file extensions. I'm also using Cloud Firestore for storing links to the uploaded files, but you can skip this.
contact.component.html
<mat-form-field>
<!--
Accept only files in the following format: .doc, .docx, .jpg, .jpeg, .pdf, .png, .xls, .xlsx. However, this is easy to bypass, Cloud Storage rules has been set up on the back-end side.
-->
<ngx-mat-file-input
[accept]="[
'.doc',
'.docx',
'.jpg',
'.jpeg',
'.pdf',
'.png',
'.xls',
'.xlsx'
]"
(change)="uploadFile($event)"
formControlName="fileUploader"
multiple
aria-label="Here you can add additional files about your project, which can be helpeful for us."
placeholder="Additional files"
title="Additional files"
type="file"
>
</ngx-mat-file-input>
<mat-icon matSuffix>folder</mat-icon>
<mat-hint
>Accepted formats: DOC, DOCX, JPG, JPEG, PDF, PNG, XLS and XLSX,
maximum files upload size: 20 MB.
</mat-hint>
<!--
Non-null assertion operators are required to let know the compiler that this value is not empty and exists.
-->
<mat-error
*ngIf="contactForm.get('fileUploader')!.hasError('maxContentSize')"
>
This size is too large,
<strong
>maximum acceptable upload size is
{{
contactForm.get('fileUploader')?.getError('maxContentSize')
.maxSize | byteFormat
}}</strong
>
(uploaded size:
{{
contactForm.get('fileUploader')?.getError('maxContentSize')
.actualSize | byteFormat
}}).
</mat-error>
</mat-form-field>
contact.component.ts (size validator part)
import { FileValidator } from 'ngx-material-file-input';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
/**
* @constructor
* @description Creates a new instance of this component.
* @param {formBuilder} - an abstraction class object to create a form group control for the contact form.
*/
constructor(
private angularFirestore: AngularFirestore,
private angularFireStorage: AngularFireStorage,
private formBuilder: FormBuilder
) {}
public maxFileSize = 20971520;
public contactForm: FormGroup = this.formBuilder.group({
fileUploader: [
'',
Validators.compose([
FileValidator.maxContentSize(this.maxFileSize),
Validators.maxLength(512),
Validators.minLength(2)
])
]
})
contact.component.ts (file uploader part)
import { AngularFirestore } from '@angular/fire/firestore';
import {
AngularFireStorage,
AngularFireStorageReference,
AngularFireUploadTask
} from '@angular/fire/storage';
import { catchError, finalize } from 'rxjs/operators';
import { throwError } from 'rxjs';
public downloadURL: string[] = [];
/**
* @description Upload additional files to Cloud Firestore and get URL to the files.
* @param {event} - object of sent files.
* @returns {void}
*/
public uploadFile(event: any): void {
// Iterate through all uploaded files.
for (let i = 0; i < event.target.files.length; i++) {
const randomId = Math.random()
.toString(36)
.substring(2); // Create random ID, so the same file names can be uploaded to Cloud Firestore.
const file = event.target.files[i]; // Get each uploaded file.
// Get file reference.
const fileRef: AngularFireStorageReference = this.angularFireStorage.ref(
randomId
);
// Create upload task.
const task: AngularFireUploadTask = this.angularFireStorage.upload(
randomId,
file
);
// Upload file to Cloud Firestore.
task
.snapshotChanges()
.pipe(
finalize(() => {
fileRef.getDownloadURL().subscribe((downloadURL: string) => {
this.angularFirestore
.collection(process.env.FIRESTORE_COLLECTION_FILES!) // Non-null assertion operator is required to let know the compiler that this value is not empty and exists.
.add({ downloadURL: downloadURL });
this.downloadURL.push(downloadURL);
});
}),
catchError((error: any) => {
return throwError(error);
})
)
.subscribe();
}
}
storage.rules
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read; // Required in order to send this as attachment.
// Allow write files Firebase Storage, only if:
// 1) File is no more than 20MB
// 2) Content type is in one of the following formats: .doc, .docx, .jpg, .jpeg, .pdf, .png, .xls, .xlsx.
allow write: if request.resource.size <= 20 * 1024 * 1024
&& (request.resource.contentType.matches('application/msword')
|| request.resource.contentType.matches('application/vnd.openxmlformats-officedocument.wordprocessingml.document')
|| request.resource.contentType.matches('image/jpg')
|| request.resource.contentType.matches('image/jpeg')
|| request.resource.contentType.matches('application/pdf')
|| request.resource.contentType.matches('image/png')
|| request.resource.contentType.matches('application/vnd.ms-excel')
|| request.resource.contentType.matches('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'))
}
}
}
I am using Angular 5.2.11, I like the solution provided by Gregor Doroschenko, however I noticed that the uploaded file is of zero bytes, I had to make a small change to get it to work for me.
postFile(fileToUpload: File): Observable<boolean> {
const endpoint = 'your-destination-url';
return this.httpClient
.post(endpoint, fileToUpload, { headers: yourHeadersConfig })
.map(() => { return true; })
.catch((e) => this.handleError(e));
}
The following lines (formData) didn't work for me.
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
https://github.com/amitrke/ngrke/blob/master/src/app/services/fileupload.service.ts