Tuesday, 4 July, 2017 UTC


Summary

So you want to allow for image uploads in your Angular 2+ apps, but would like to limit the dimension of uploaded images directly on the frontend before even uploading anything? The ng2-img-max module is just what you need! ng2-img-max will use web workers when available to perform the resizing computations, leaving the main thread alone.
Let’s go over its use:
Installation
First, install the module using npm or Yarn:
$ npm install ng2-img-max blueimp-canvas-to-blob --save # or Yarn: $ yarn add ng2-img-max blueimp-canvas-to-blob
blueimp-canvas-to-blob is a polyfill needed in order for canvas.toBlob() to be available on browsers like Safari and and older versions of Internet Explorer.
Include the polyfill script in your project. If you’re using the Angular CLI, you can just add the script in your .angular-cli.json file:
.angular-cli.json
... "scripts": [ "../node_modules/blueimp-canvas-to-blob/js/canvas-to-blob.min.js" ],
You'll want to restart your local server after adding a script to the Angular CLI configuration.

Now let’s import the module in you app module or into a feature module:
app.module.ts
// ... import { Ng2ImgMaxModule } from 'ng2-img-max'; @NgModule({ declarations: [AppComponent], imports: [ // ... Ng2ImgMaxModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
And finally, the ng2-img-max service can be imported and injected in a component like this:
app.component.ts
import { Component } from '@angular/core'; import { Ng2ImgMaxService } from 'ng2-img-max'; @Component({ ... }) export class AppComponent { constructor(private ng2ImgMax: Ng2ImgMaxService) {} }
Usage
Let’s add a file input to our component’s template like this:
app.component.html
<input type="file" (change)="onImageChange($event)" accept="image/*"> 
And define the onImageChange method in the component class. This will limit the image to a width of 400px and a height of 300px:
app.component.ts
uploadedImage: Blob; constructor(private ng2ImgMax: Ng2ImgMaxService) {} onImageChange(event) { let image = event.target.files[0]; this.ng2ImgMax.resizeImage(image, 400, 300).subscribe( result => { this.uploadedImage = result; }, error => { console.log('😢 Oh no!', error); } ); }
If you have multiple images to resize at once, use the resize method instead and pass-in an array of image files as the first argument.
The result is of type Blob, but you can convert that to a proper file using the File constructor like this if you want:
app.component.ts
uploadedImage: File; constructor(private ng2ImgMax: Ng2ImgMaxService) {} onImageChange(event) { let image = event.target.files[0]; this.ng2ImgMax.resizeImage(image, 400, 300).subscribe( result => { this.uploadedImage = new File([result], result.name); }, error => { console.log('😢 Oh no!', error); } ); }
You can now upload the file to your backend. Don't forget to validate things on the backend side, because nothings here prevents some conniving user from uploading oversized or non-image files directly to your backend.

Limiting only width or height

Say you want to only limit the height to 300px, and have the width resized accordingly to keep the aspect ratio the same. Just set whichever side should just fallow to a value of 10000:
app.component.ts
// ... onImageChange(event) { let image = event.target.files[0]; this.ng2ImgMax.resizeImage(image, 10000, 300).subscribe( result => { this.uploadedImage = new File([result], result.name); }, error => { console.log('😢 Oh no!', error); } ); }

Compression Instead of Resizing

You can also use the compress or compressImage methods to perform lossy compression instead of resizing the image. Just pass-in a maximum value in megabytes. You’ll obviously want to run some tests to see how small you’ll want to go here while still keeping the images looking good to the eye.
In the following example, we’re limiting the resulting image to about 75Kb:
onImageChange(event) { let image = event.target.files[0]; this.ng2ImgMax.compressImage(image, 0.075).subscribe( result => { this.uploadedImage = new File([result], result.name); this.getImagePreview(this.uploadedImage); }, error => { console.log('😢 Oh no!', error); } ); }
Image Preview
You’ll probably want to give a preview of the image to be uploaded to your users. You can do this using the FileReader object. You’ll also need to use Angular’s DomSanitizer to tell it to trust the base64-encoded data URI created using the FileReader object:
Here’s what our component now looks like. The interesting new method here is getImagePreview:
app.component.ts
import { Component } from '@angular/core'; import { Ng2ImgMaxService } from 'ng2-img-max'; import { DomSanitizer } from '@angular/platform-browser'; @Component({ ... }) export class AppComponent { uploadedImage: File; imagePreview: string; constructor( private ng2ImgMax: Ng2ImgMaxService, public sanitizer: DomSanitizer ) {} onImageChange(event) { let image = event.target.files[0]; this.ng2ImgMax.resizeImage(image, 10000, 375).subscribe( result => { this.uploadedImage = new File([result], result.name); this.getImagePreview(this.uploadedImage); }, error => { console.log('😢 Oh no!', error); } ); } getImagePreview(file: File) { const reader: FileReader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { this.imagePreview = reader.result; }; } }
And in our template we can use the sanitizer to display the image like this:
app.component.html
<img *ngIf="imagePreview" [src]="sanitizer.bypassSecurityTrustUrl(imagePreview)"> 
😄 And that's all there is to it! You can also check out the ng2-img-tools package by the same author for more browser-side image manipulation like cropping.