前言
上传功能在任何一个网站中的地位都是举足轻重的,这篇文章主要扯下如何实现一个上传组件
效果图
所具有的功能
- 支持的图片格式(不传参则使用默认参数)
- 支持的图片大小
- 图片上传之前会被压缩(前端) -- 异步加载进来
- 上传过程会显示loading(loading组件)--就一些css3样式
- 支持组件高度设置,宽度自适应
- 支持标题设置
组件以模块形式导出
代码如下,相关逻辑请看注释。
mit-upload.module.ts
-- 上传模块
// 这三个就不用再解释了
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
// 服务
import { MitImageUploadLoaderService } from './services/mit-image-upload.loader.service'; // 异步加载JS
import { MitImageUploadService } from './services/mit-image-upload.service';
const service = [
MitImageUploadLoaderService,
MitImageUploadService
];
// 页面
import { MitUploadComponent } from './mit-upload.component';
// 组件 -- loading
import { MitLoadingModule } from '../mit-loading/mit-loading.module';
const component = [
MitUploadComponent
];
@NgModule({
imports: [
FormsModule,
CommonModule,
MitLoadingModule
],
declarations: [
...component
],
exports: [
...component
],
providers: [
...service
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class MitUploadModule { }复制代码
mit-upload.component.html
--- 组件的html结构
<div class="image-upload" [style.height]="height + 'px'" [ngClass]="{'upload-fail': uploadStatus}">
<div class="upload-area">
<input type="file" class="upload-input" (change)="selected($event)">
<div class="icon-wrap">
<i class="fpd fpd-upload"></i>
</div>
<p class="upload-tips">{{uploadDesrc}}</p>
<div class="img-preview" *ngIf="preview">
<img [src]="preview" class="res-img" alt="" onError="this.src='assets/images/default_img/img_error_load.png';">
</div>
<app-mit-loading [option]="'load1'" style="position:absolute;top:0;left:0;width:100%;height:100%;background: rgba(72, 72, 72, 0.65);"
*ngIf="loadingStatus"></app-mit-loading>
</div>
<div class="upload-footer" *ngIf="uploadTitleName">
<span>{{uploadTitleName}}</span><a href="javascript:;" (click)="delete($event)" *ngIf="preview">删除</a>
</div>
</div>复制代码
mit-upload.component.scss
-- 组件样式(scss)
@charset 'UTF-8';
// 自定义的一些mixin什么的。。
@import '../../../assets/scss_styles/custom_scss/_custom-export.scss';
$iu-border:#e7e7e7 !default; // 边框颜色
$iu-icon-color:#d5d4d4 !default;
.upload-fail {
.upload-area {
border: 2px solid #d9534f !important;
.icon-wrap {
i {
color: #d9534f !important;
}
}
.upload-tips {
color: #d9534f;
}
}
}
.image-upload {
width: 100%;
height: 100%;
position: relative;
.upload-area {
position: relative;
width: 100%;
height: 100%;
border: 2px solid $iu-border;
@include flex(center, center); // flex的mixin,就是水平垂直居中
flex-wrap: wrap;
.icon-wrap {
width: 100%;
@include flex(center, center);
i {
font-size: 64px;
color: $iu-icon-color;
}
}
p {
margin: 0;
padding: 0.5rem;
font-size: 14px;
color: #808080;
}
.upload-input {
cursor:pointer;
position: absolute;
left: 0;
top: 0;
opacity: 0;
height: 100%;
width: 100%;
background: transparent;
z-index: $zindex-xs;
}
.img-preview {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: $zindex-md;
img {
width: 100%;
height: 100%;
}
}
}
.upload-footer {
padding: 0.5rem;
@include flex(space-between, center); // 两边对齐,垂直剧中
span {
font-size: 14px;
color: #313131;
}
a {
font-size: 14px;
color: #37c2dd;
}
}
}
.res-img {
width: 100%;
}复制代码
mit-image-upload.loader.service.ts
-- 异步加载前端图片压缩的脚本
用到的是一个github上库:localResizeIMG;
我这里下载了放在cdn上。。
import { Injectable } from '@angular/core';
@Injectable()
export class MitImageUploadLoaderService {
constructor() { }
// 使用promise异步获取回调后动态插入该脚本
load(): Promise<any> {
const LRZ_URL = 'http://xxxxxxx.bkt.clouddn.com/lrz.all.bundle.js';// xxxx是我随意打,我放在七牛上了
const p = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.type = 'text/javascript';
script.setAttribute('src', LRZ_URL);
script.onload = resolve;
script.async = true;
document.head.appendChild(script);
});
return p;
}
}复制代码
mit-image-upload.service.ts
-- 处理图片上传(接口)
import { Injectable } from '@angular/core'; // 核心库-注入服务
import { AuthService } from '../../../services/auth.service'; // 鉴权
import { environment } from '../../../../environments/environment'; // 环境变量
// 图片上传接口
import { IMitImageUpload } from '../interface/mit-image-upload.model';
@Injectable()
export class MitImageUploadService {
constructor(private authHttp: AuthService) { }
resize(e): Promise<any> {
const lrz = (<any>window)['lrz'];
const p = new Promise((resolve, reject) => {
lrz(e)
.then(function (rst) {
resolve(rst);
})
.catch(function (err) {
// 处理失败会执行
reject(err);
})
.always(function () {
// 不管是成功失败,都会执行
});
});
return p;
}
uploadImg(iMitImageUploadParam: IMitImageUpload) {
// authHttp已经做了一些鉴权的封装(对内置的http模块)
return this.authHttp.upload(environment.baseUrl + 'FileUpload/ImgUpload', iMitImageUploadParam);
}
}复制代码
environment.ts
-- 存放限制规格参数的。。
我们这里是考虑environment
这个来存放各种配置相关的信息,所以就独立出来了,正常逻辑是封装到组件内的。
// 图片上传参数
export const uploadImgParam = {
'fileType': ['image/png', 'image/jpeg', 'image/jpg'], // 图片上传格式
'fileSize': 3, // 图片上传大小限制(MB)
};复制代码
mit-upload.component.ts
--- 上传逻辑的实现
import { Component, OnInit, Input, Output, EventEmitter, ElementRef, HostListener, AfterViewInit } from '@angular/core';
import { MitImageUploadLoaderService } from './services/mit-image-upload.loader.service';
import { MitImageUploadService } from './services/mit-image-upload.service';
import { uploadImgParam } from '../../../environments/environment'; // 上传配置
@Component({
selector: 'app-mit-upload',
templateUrl: './mit-upload.component.html',
styleUrls: ['./mit-upload.component.scss']
})
export class MitUploadComponent implements OnInit, AfterViewInit {
@Input() uploadTitleName: string; // 上传标题字
@Input() height: any; // 定制高度
@Input() valiScope?: any; // 上传限制条件
@Input() uploadType: string; // 限定上类型
@Output() uploadEvt = new EventEmitter();
@Input() preview: any; // 图片预览
public uploadDesrc = '请点击上传'; // 点击上传文字替换
public loadingStatus = false; // loading
public uploadStatus = false; // 上传状态样式高亮
constructor(
private loader: MitImageUploadLoaderService,
private mitImageUploadService: MitImageUploadService
) { }
ngOnInit() {
// console.log( this.uploadTitleName );
}
ngAfterViewInit() {
const lrz = (<any>window)['lrz'];
if (!lrz) {
this.loader.load().then(() => {
// console.log( 'lrz异步加载成功' );
// console.log(( <any>window )[ 'lrz' ] );
}).catch(() => { });
} else {
// console.log( 'lrz无需异步加载' );
}
}
onerror(e) {
// console.log( e );
}
// 选择文件
selected(e) {
this.fileValidator(e.target.files[0], this.uploadType);
}
// 上传格式限制判断
fileValidator(e, uploadType?) {
// console.log( e );
// [valiScope]="{'imageType':['image/png','image/jpeg','image/jpg'],'imageSize':3}"
const scope = {
type: [],
size: 3
};
const filename = e.name;
const filetype = e.type;
const filesize = e.size;
if (this.valiScope) {
scope.type = this.valiScope.Type; // 限制的文件格式
scope.size = this.valiScope.Size * 1024 * 1024; // 限制的大小
} else {
scope.type = uploadImgParam.fileType;
scope.size = uploadImgParam.fileSize * 1024 * 1024;
}
// console.log( scope.type );
if (e && scope.type.indexOf(filetype) === -1) {
this.uploadDesrc = '图片格式不匹配';
this.uploadStatus = true;
} else if (e && scope.size < filesize) {
this.uploadDesrc = '图片大小不匹配';
this.uploadStatus = true;
} else {
this.uploadStatus = false;
this.loadingStatus = true;
this.resize(e);
}
return null;
}
// reize
resize(e) {
if (e) {
this.mitImageUploadService.resize(e).then((res) => {
this.uploadIMG(res.formData); // 调用上传接口
});
}
}
// 上传图片
uploadIMG(data) {
this.mitImageUploadService.uploadImg(data).subscribe((res) => {
console.log(res);
this.loadingStatus = false;
if (res.State) {
this.preview = res.Data; // 回调成功后渲染图片
this.uploadEvt.emit(res.Data);
} else {
this.uploadStatus = true;
this.uploadDesrc = res.Message;
}
}, (error) => {
this.loadingStatus = false;
this.uploadStatus = true;
this.uploadDesrc = '上传失败请重试';
});
}
// 删除图片预览
delete(e) {
// console.log(e);
this.preview = null;
}
}复制代码
总结
- 我们的项目一些库都使用cdn来存放一些静态文件,不仅有利于减少打包体积,也有利于加载速度!!
至此,一个不怎么靠谱的上传组件诞生了,你可以在这个基础二次定制;
有更好的写法或者建议的也可以留言指出,谢谢