27

I want to encode the uploaded files to base64 so that I can pass them to the request. The problem is that I'm using Angular 2 with Typescript and I couldn't find any info on how to do that. I found that in Javascript it can be done with canvas but I don't know how could I implement the code in Typescript.

<input type="file" class="form-control" accept="image/*" multiple
    [(ngModel)]="spot.images" name="images">
Mantas
  • 1,223
  • 1
  • 10
  • 15

4 Answers4

74

So I find the solution:

compontent.ts

changeListener($event) : void {
  this.readThis($event.target);
}

readThis(inputValue: any): void {
  var file:File = inputValue.files[0];
  var myReader:FileReader = new FileReader();

  myReader.onloadend = (e) => {
    this.image = myReader.result;
  }
  myReader.readAsDataURL(file);
}

component.html

<input type="file" accept="image/*" (change)="changeListener($event)">
Mantas
  • 1,223
  • 1
  • 10
  • 15
8

Here is the answer above wrapped in a reuseable component that ties into ngmodel.

import { NgModule, Component, Input, Output, ElementRef, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { FormsModule } from "@angular/forms";

@Component({
    selector: 'file-upload',
    template:  `<input *ngIf="showFileNameInput" id="uploadFile" class="upload-file form-control" placeholder="Choose File" [(ngModel)]="selectedFileName" disabled="disabled" />
                <div class="fileUpload btn btn-primary">
                    <span>{{uploadButtonText}}</span>
                    <input type="file" class="upload" accept="*" (change)="changeListener($event)">
                </div>`,
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => FileUploadComponent),
            multi: true
        }
    ]
})
export class FileUploadComponent implements ControlValueAccessor {
    selectedFileName: string = null;
    @Input() showFileNameInput: boolean;
    @Input() uploadButtonText: string;

    writeValue(value: any) {
       //Handle write value
    }
    propagateChange = (_: any) => { };
    registerOnChange(fn) {
        this.propagateChange = fn;
    }
    registerOnTouched() { }

    changeListener($event): void {
        // debugger; // uncomment this for debugging purposes
        this.readThis($event.target);
    }
    readThis(inputValue: any): void {
        // debugger; // uncomment this for debugging purposes
        var file: File = inputValue.files[0];
        var myReader: FileReader = new FileReader();

        myReader.onloadend = (e) => {
            this.propagateChange(myReader.result);
            this.selectedFileName = file.name;
        }
        myReader.readAsDataURL(file);
    }
}

@NgModule({
    declarations: [
        FileUploadComponent
    ],
    imports: [FormsModule],
    exports: [
        FileUploadComponent
    ]
})
export class FileUploadModule { }

Which can be used like

<file-upload [showFileNameInput]="true" allowedTypes="image/*" uploadButtonText="Upload File" [(ngModel)]="someProperty"></file-upload> 

Also some css that helped it blend into bootstrap on my site

/********************************/
/* File Upload */
.fileUpload {
    position: relative;
    overflow: hidden;
}

.fileUpload input.upload {
    position: absolute;
    top: 0;
    right: 0;
    margin: 0;
    padding: 0;
    font-size: 20px;
    cursor: pointer;
    opacity: 0;
    filter: alpha(opacity=0);
}

.upload-file {
    &.form-control {
        width: auto;
        display: inherit;
    }
}
Raymond Ativie
  • 1,747
  • 2
  • 26
  • 50
Josh
  • 1,648
  • 8
  • 27
  • 58
  • how can you pass this to a request? What is the base64 value in here? – Mix Austria Feb 09 '17 at 22:04
  • @MixAustria The base64 value is being output to whatever is bound to ngModel or formControl of the component. You can use it like this The Base 64 string will be in "someProperty" on your component – Josh Feb 09 '17 at 23:09
  • do you mean `someProperty` is a variable inside the component? I'm sorry I am really new at this. – Mix Austria Feb 09 '17 at 23:40
  • @MixAustria Yes someProperty is a variable in your component. – Josh Feb 10 '17 at 01:18
  • @Josh I am getting this error when using this inside the form, If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions, how to use it inside form so that it can be posted over the serve – Inam Hassan Feb 22 '17 at 20:11
  • @InamHassan Give it a name? i.e. something like this: . Again, someProperty is a variable in your component. – Dave Nottage Mar 21 '17 at 04:10
  • There's a typo in @Josh's answer & comment - `[(ngModel)]]="someProperty"` should be `[(ngModel)]="someProperty"` – tylerl Jun 18 '17 at 22:50
5

You can create a Wrapper class for the FileReader class to return an observable.Subscribe for it and on success use the .target to get the base64 for do whatever you want.

import {ReplaySubject} from "rxjs/ReplaySubject";
import {Observable} from "rxjs/Observable";

export class ObservableFileReader {

  constructor(){}

  public readFile(fileToRead: File): Observable<MSBaseReader>{
    let base64Observable = new ReplaySubject<MSBaseReader>(1);

    let fileReader = new FileReader();
    fileReader.onload = event => {
        base64Observable.next(fileReader.result);
    };
    fileReader.readAsDataURL(fileToRead);

    return base64Observable;
   }
}
Alex Simons
  • 38
  • 1
  • 4
Valex
  • 1,149
  • 1
  • 8
  • 14
3

A possible solution using Rxjs

  import { fromEvent } from 'rxjs';
  import { pluck } from 'rxjs/operators';

   onUploadImage(event) {
    if (event.target.files.length > 0) {
      const fileReader = new FileReader();
      let imageToUpload = event.target.files.item(0);
      this.imageToBase64(fileReader, imageToUpload)
        .subscribe(base64image => {
          // do something with base64 image..
        });
    }
  }

  imageToBase64(fileReader: FileReader, fileToRead: File): Observable<string> {
    fileReader.readAsDataURL(fileToRead);
    return fromEvent(fileReader, 'load').pipe(pluck('currentTarget', 'result'));
  }
user2972221
  • 376
  • 3
  • 15