压缩图像后 reader.onload 不会重置

时间:2021-06-16 09:29:36

标签: angular filereader

我使用 <input type="file" multiple (change)="selectFiles($event)">

上传图片

然后我用这个显示图像列表:

const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
  if (typeof reader.result === 'string') {
   this.files.push({
      filename: file.name,
      content: reader.result.split(',')[1]
    } as ComplaintAttachmentInterface);
  }
};
reader.onerror = (error) => {
  console.log('Error: ', error);
};

和:

<ng-container *ngIf="files.length">
  <p-table [value]="files" styleClass="p-datatable-sm">
    <ng-template pTemplate="body" let-file let-rowIndex="rowIndex">
      <tr>
    <td>{{file.filename}}</td>
    <td class="text-center"  style="width:50px;">
      <i class="fas fa-trash cursor-pointer" (click)="removeFile(rowIndex)"></i>
    </td>
  </tr>
</ng-template>
 </p-table>
</ng-container>

每次添加图像时,列表都会毫无问题地刷新。

但我想通过服务压缩图像,当我压缩图像时,列表不再刷新,我必须单击以显示添加的图像,但是如果我从列表中创建一个控制台 console.log(this.files.length) ,它告诉我列表包含元素,但未完成 html 端的更新。

如果有人知道压缩图像后重新激活列表的解决方案吗?

这是压缩服务:

compress-image.service

<块引用>
  import { Injectable } from '@angular/core' import { Observable } from
  'rxjs'

  // in bytes, compress images larger than 1MB const fileSizeMax = 1 *
  1024 * 1024 // in pixels, compress images have the width or height
  larger than 1024px const widthHeightMax = 1024 const
  defaultWidthHeightRatio = 1 const defaultQualityRatio = 0.7

   @Injectable({   providedIn: 'root' }) export class
   CompressImageService {   compress(file: File): Observable<File> {
   const imageType = file.type || 'image/jpeg' || 'image/png'
   const reader = new FileReader()
   reader.readAsDataURL(file)

   return Observable.create(observer => {
  // This event is triggered each time the reading operation is successfully 
   completed.
  reader.onload = ev => {
    // Create an html image element
    const img = this.createImage(ev)
    // Choose the side (width or height) that longer than the other
    const imgWH = img.width > img.height ? img.width : img.height

    // Determines the ratios to compress the image
    let withHeightRatio = (imgWH > widthHeightMax) ? widthHeightMax/imgWH : defaultWidthHeightRatio
    let qualityRatio = (file.size > fileSizeMax) ? fileSizeMax/file.size : defaultQualityRatio

    // Fires immediately after the browser loads the object
    img.onload = () => {
      const elem = document.createElement('canvas')
      // resize width, height
      elem.width = img.width * withHeightRatio
      elem.height = img.height * withHeightRatio


      const ctx = <CanvasRenderingContext2D>elem.getContext('2d')
      ctx.drawImage(img, 0, 0, elem.width, elem.height)
      ctx.canvas.toBlob(
        // callback, called when blob created
        blob => {
          observer.next(new File(
            [blob],
            file.name,
            {
              type: imageType,
              lastModified: Date.now(),
            }
          ))
        },
        imageType,
        qualityRatio, // reduce image quantity
      )
    }
  }

  // Catch errors when reading file
  reader.onerror = error => observer.error(error)
})   }

   private createImage(ev) {
    let imageContent = ev.target.result
    const img = new Image()
    img.src = imageContent
    return img   } }

file.component.ts

 import { Component, Input } from '@angular/core';
 import { FileAttachmentInterface } from '../../../../interfaces/file- 
 attachment.interface';
 import { TranslateService } from '@ngx-translate/core';
 import { CompressImageService } from '../../../../service/compress-image.service';
 import { take } from 'rxjs/operators';

  @Component({
   selector: 'file-attachment',
   templateUrl: './file-attachment.component.html',

   })
  export class FileAttachmentComponent {
  @Input() public files: FileAttachmentInterface[] = [];
  @Input() public maxSizeAttachment = 2097152;
  private authorizedTypes: string[] = ['image/jpeg', 'image/png', 'application/pdf'];
  public errors: string[] = [];

  constructor(private _translateSrvc: TranslateService, private compressImage: 
 CompressImageService) {}

 public selectFiles(event): void {
  this.addFiles(event.target.files);
 }

   public removeFile(index: number) {
     this.files.splice(index, 1);
   }

   private addFiles(files: FileList): void {
     this.errors.length = 0;
     Array.from(files).forEach((file) => {
   if(file.type ==="image/jpeg" || file.type ==="image/png") {
     console.log(`Image size before compressed: ${file.size} bytes.`)
      this.compressImage.compress(file)
        .pipe(take(1))
        .subscribe(compressedImage => {
         this.add(compressedImage);
        })
     } else {
    this.add(file);
      }
     });
   }

  private add(file: File): void {

if (!this.authorizedTypes.includes(file.type)) {
  this.errors.push(this._translateSrvc.instant('MODAL.FILE.ERROR_FILE_EXTENSION', {
      file: file.name
    }));
  return;
}
if (file.size > this.maxSizeAttachment) {
    this.errors.push(this._translateSrvc.instant('MODAL.FILE.ERROR_FILE_SIZE', {
      file: file.name,
      size: this.formatBytes(file.size)
    }));
  return;
}

const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
  if (typeof reader.result === 'string') {
    if (this.files.length > 9) {
      this.errors.push(this._translateSrvc.instant('MODAL.FILE.ERROR_MAX_FILES', {
        file: file.name
      }));
      return;
    }
    this.files.push({
      filename: file.name,
      content: reader.result.split(',')[1]
    } as FileAttachmentInterface);
    console.log(this.files.length)
  }
};
reader.onerror = (error) => {
  console.log('Error: ', error);
};
}

private formatBytes = (numberInBytes, decimal = 2): string => {
if (0 === numberInBytes) {
  return '0 Bytes';
 }
 const c = 0 > decimal ? 0 : decimal;
 const d = Math.floor(Math.log(numberInBytes) / Math.log(1024));
 return (
  parseFloat((numberInBytes / Math.pow(1024, d)).toFixed(c)) +
  ' ' +
  ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][d]
  );
 };
}

file.component.html

<div class="d-none d-lg-flex">
 <input type="file" multiple (change)="selectFiles($event)">
 </div>
</div>

 <ng-container *ngIf="files.length">
  <p-table [value]="files" styleClass="p-datatable-sm">
   <ng-template pTemplate="body" let-file let-rowIndex="rowIndex">
     <tr>
      <td>{{file.filename}}</td>
      <td class="text-center"  style="width:50px;">
      <i class="fas fa-trash cursor-pointer" (click)="removeFile(rowIndex)"></i>
      </td>
   </tr>
    </ng-template>
   </p-table>
 </ng-container>

1 个答案:

答案 0 :(得分:0)

经过一番挖掘,我找到了一个可以更新上传图片列表的解决方案。

如果我在 this.ref.detectChanges(); 中添加:reader.onload() 这会更新列表

file.component.ts

 import { ChangeDetectorRef, Component, Input } from '@angular/core';
 import { FileAttachmentInterface } from '../../../../interfaces/file- 
 attachment.interface';
 import { TranslateService } from '@ngx-translate/core';
 import { CompressImageService } from '../../../../service/compress-image.service';
 import { take } from 'rxjs/operators';

  @Component({
   selector: 'file-attachment',
   templateUrl: './file-attachment.component.html',

   })
  export class FileAttachmentComponent {
  @Input() public files: FileAttachmentInterface[] = [];
  @Input() public maxSizeAttachment = 2097152;
  private authorizedTypes: string[] = ['image/jpeg', 'image/png', 'application/pdf'];
  public errors: string[] = [];

  constructor(private _translateSrvc: TranslateService, private compressImage: 
 CompressImageService, private ref: ChangeDetectorRef) {}

 public selectFiles(event): void {
  this.addFiles(event.target.files);
 }

   public removeFile(index: number) {
     this.files.splice(index, 1);
   }

   private addFiles(files: FileList): void {
     this.errors.length = 0;
     Array.from(files).forEach((file) => {
   if(file.type ==="image/jpeg" || file.type ==="image/png") {
     console.log(`Image size before compressed: ${file.size} bytes.`)
      this.compressImage.compress(file)
        .pipe(take(1))
        .subscribe(compressedImage => {
         this.add(compressedImage);
        })
     } else {
    this.add(file);
      }
     });
   }

  private add(file: File): void {

if (!this.authorizedTypes.includes(file.type)) {
  this.errors.push(this._translateSrvc.instant('MODAL.FILE.ERROR_FILE_EXTENSION', {
      file: file.name
    }));
  return;
}
if (file.size > this.maxSizeAttachment) {
    this.errors.push(this._translateSrvc.instant('MODAL.FILE.ERROR_FILE_SIZE', {
      file: file.name,
      size: this.formatBytes(file.size)
    }));
  return;
}

const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
  if (typeof reader.result === 'string') {
    if (this.files.length > 9) {
      this.errors.push(this._translateSrvc.instant('MODAL.FILE.ERROR_MAX_FILES', {
        file: file.name
      }));
      return;
    }
    this.files.push({
      filename: file.name,
      content: reader.result.split(',')[1]
    } as FileAttachmentInterface);
   }
this.ref.detectChanges();
};
reader.onerror = (error) => {
  console.log('Error: ', error);
};
}

private formatBytes = (numberInBytes, decimal = 2): string => {
if (0 === numberInBytes) {
  return '0 Bytes';
 }
 const c = 0 > decimal ? 0 : decimal;
 const d = Math.floor(Math.log(numberInBytes) / Math.log(1024));
 return (
  parseFloat((numberInBytes / Math.pow(1024, d)).toFixed(c)) +
  ' ' +
  ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][d]
  );
 };
}
相关问题