Angular 2 + 折腾记 :(8) 动手写一个不怎么靠谱的上传组件

前言

上传功能在任何一个网站中的地位都是举足轻重的,这篇文章主要扯下如何实现一个上传组件


效果图


所具有的功能

  1. 支持的图片格式(不传参则使用默认参数)
  2. 支持的图片大小
  3. 图片上传之前会被压缩(前端) – 异步加载进来
  4. 上传过程会显示loading(loading组件)–就一些css3样式
  5. 支持组件高度设置,宽度自适应
  6. 支持标题设置

组件以模块形式导出

代码如下,相关逻辑请看注释。

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

}


总结

  1. 我们的项目一些库都使用cdn来存放一些静态文件,不仅有利于减少打包体积,也有利于加载速度!!

至此,一个不怎么靠谱的上传组件诞生了,你可以在这个基础二次定制;

有更好的写法或者建议的也可以留言指出,谢谢

时间: 2024-10-18 03:07:25

Angular 2 + 折腾记 :(8) 动手写一个不怎么靠谱的上传组件的相关文章

动手写一个Remoting测试工具

基于.NET开发分布式系统,经常用到Remoting技术.在测试驱动开发流行的今天,如果针对分布式系统中的每个Remoting接口的每个方法都要写详细的测试脚本,无疑非常浪费时间.所以,我想写一个能自动测试remoting接口的小工具InterfaceTester.而且,当分布式系统中的某个remoting接口出现bug时,该小工具可以提交需要模拟的数据,以便在调试remoting服务的环境中,快速定位和解决bug. InterfaceTester运行起来后的效果如下图: 1.如何使用 (1)首

模拟spring - 动手写一个spring AOP

一.前言 AOP (Aspect Oriented Programing) - 面向切面编程,它主要用于日志记录.性能分析.安全控制.事务处理.异常处理等方面. AOP主要使用JDK的反射和动态代理,AOP代理其实是由AOP框架动态生成的一个对象,该对象可作为目标对象使用,AOP代理包含了目标对象的全部方法,但AOP代理的方法与目标对象的方法存在差异:AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法. 动态代理的文章请参考:http://blog.csdn.net/zdp072/ar

死磕 java线程系列之自己动手写一个线程池

欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写的线程池如何测试? 简介 线程池是Java并发编程中经常使用到的技术,那么自己如何动手写一个线程池呢?本文彤哥将手把手带你写一个可用的线程池. 属性分析 线程池,顾名思义它首先是一个"池",这个池里面放的是线程,线程是用来执行任务的. 首先,线程池中的线程应该是有类别的,有的是核心线程,有

操刀 requirejs,自己动手写一个

前沿 写在文章的最前面 这篇文章讲的是,我怎么去写一个 requirejs . 去 github 上fork一下,顺便star~ requirejs,众所周知,是一个非常出名的js模块化工具,可以让你使用模块化的方式组织代码,并异步加载你所需要的部分.balabala 等等好处不计其数. 之所以写这篇文章,是做一个总结.目前打算动一动,换一份工作.感谢 一线码农 大大帮忙推了携程,得到了面试的机会. 面试的时候,聊着聊着感觉问题都问在了自己的“点”上,应答都挺顺利,于是就慢慢膨胀了.在说到模块化

实际动手写一个商城购物系统

动手需求分析,希望巩固自己已经有的技术,并且如果在开发中遇到新技术,便于学习新技术. 本系统分2大部分,跟大众系统的功能差不多,一个是后台管理系统:商家管理后台登录用户,商家详细信息管理,商品的上架,下架,信息修改,查询所有商品的功能,统计商品的销售情况.另一个是前台的商品展示页面,包括购买者的注册,登录,购物车,商品展示,商品详细信息浏览等. 数据库和数据库表只能系统搭建好后慢慢分析了. 运用的工具和技术:eclipse4.3+tomcat7+mysql   springMVC+mybatis

记一个使用Client Object Model上传文件的小例子

1. 新建一个C#的Console project. 2. 给project 添加reference: Microsoft.SharePoint.Client Microsoft.SharePoint.Runtime 3. 修改project的属性: Platform target – x64 Target framework – .NET Framework 4 4. 修改代码如下: using System; using System.IO; using System.Net; using

[WebApi] 捣鼓一个资源管理器--多文件上传+数据库辅助

<打造一个网站或者其他网络应用的文件管理接口(WebApi)第三章"多文件上传+数据库辅助存储"> ======================================================== 作者:qiujuer 博客:blog.csdn.net/qiujuer 网站:www.qiujuer.net 开源库:Genius-Android 转载请注明出处: http://blog.csdn.net/qiujuer/article/details/4172

使用plupload做一个类似qq邮箱附件上传的效果

公司项目中使用的框架是springmvc+hibernate+spring,目前需要做一个类似qq邮箱附件上传的功能,暂时只是上传小类型的附件 处理过程和解决方案都需要添加附件,处理过程和解决方案都可以添加多个附件,也可一个都不添加 以其中一个为例:(文件保存到了数据库中),有关plupload的内容可参考:http://www.360doc.com/content/14/0714/03/552866_394228686.shtml 首先是po package cn.com.plupload.p

自己动手写一个FTP客户端

自己用socket写一个FTP客户端,模拟主动被动模式.(先支持LIST命令) # -*- coding: utf-8 -*- import socket, sys, thread, threading def main_sock(daddr, actions, saddr=()):     if saddr:         try:             sc=socket.create_connection(daddr, 3, saddr)             #print "Now