Angular 2 将图像编码为 base64

27

我想将上传的文件编码为base64,以便将它们传递给请求。问题在于我正在使用Angular 2和Typescript,我找不到任何关于如何执行此操作的信息。我发现在Javascript中可以使用canvas来完成,但我不知道如何在Typescript中实现该代码。

<input type="file" class="form-control" accept="image/*" multiple
    [(ngModel)]="spot.images" name="images">

你为什么不使用FileReader.readAsDataURL()呢? - Georg Grab
有任何解决方案吗?我也需要这个。 - UzUmAkI_NaRuTo
工作代码:https://stackblitz.com/edit/encode-base64-img?file=src%2Fapp%2Fapp.component.html - Billu
4个回答

74

所以我找到了解决方案:

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);
}

组件.html

<input type="file" accept="image/*" (change)="changeListener($event)">

这个完美地运作了。我甚至能够将它包装在一个组件中,以便与ngmodel相结合,将其用作表单控件。如果您感兴趣,请查看我的答案。 - Josh
你好!多个文件怎么样? - Edwin Bermejo
3
太棒了!我用你的解决方案创建了一个 StackBlitz:https://stackblitz.com/edit/encode-base64-img - hmartos
我遇到了一个错误,ReferenceError: e未定义。 - rakshith

8

这里是一个可重复使用的组件,与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 { }

可以像这样使用

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

同时还有一些CSS,可以帮助它更好地融入我的网站中的Bootstrap。

/********************************/
/* 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;
    }
}

你怎么将这个传递给请求?这里的base64值是什么? - Mix Austria
@MixAustria Base64值将输出到组件的ngModel或formControl绑定的任何内容。您可以像这样使用它: Base 64字符串将在组件的“someProperty”中。 - Josh
@MixAustria 是的,someProperty是你组件中的一个变量。 - Josh
@Josh 当我在表单内使用这个时,出现了这个错误。如果在 form 标签内使用 ngModel,则必须设置 name 属性或将表单控件定义为 'standalone' 在 ngModelOptions 中。请问如何在表单内使用它,以便可以将其发布到服务器上? - Inam Hassan
@Josh的回答和评论中有一个错别字 - [(ngModel)]]="someProperty" 应该是 [(ngModel)]="someProperty" - tylerl
显示剩余2条评论

5
你可以创建一个包装器类来为FileReader类返回Observable对象。订阅它,成功后使用.target获取base64以执行任何你想要的操作。
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;
   }
}

1
你能否添加一段代码片段呢?我在这里不太明白。 - Mantas

3
使用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'));
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接