我使用 <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>
答案 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]
);
};
}