ionic5 上传图片,并附加裁剪功能

ionic上传图片总共分3个步骤:
(1)通过插件选取图片,或者调用手机相机拍摄然后获取图片
(2)将获取到的图片进行编辑,裁剪
(3)将裁剪后的图片利用ajax上传至服务器

获取图片

  1. 通过 cordova-plugin-camera 插件,可进行拍照,或是从图库中选择图片
ionic cordova plugin add cordova-plugin-camera
npm install @ionic-native/camera
import { Camera, CameraOptions } from '@ionic-native/camera/ngx';

constructor(private camera: Camera) { }

...


const options: CameraOptions = {
  quality: 100, // 这里图片质量最好不要100 , 会导致有些图片上传不成功
  destinationType: this.camera.DestinationType.FILE_URI,
  encodingType: this.camera.EncodingType.JPEG,
  mediaType: this.camera.MediaType.PICTURE
}

this.camera.getPicture(options).then((imageData) => {
	// 为了保证在低内存手机使用正常,获取路径类型建议不要选择Base64,否则会导致手机闪退崩溃
 	// 这里获取出来的路径,是带“file://”协议的,
}, (err) => {
 // Handle error
});

转换图片路径

  1. 将获取到的文件路径通过 cordova-plugin-file 转化成 cdvfile:// 协议:
ionic cordova plugin add cordova-plugin-file
npm install @ionic-native/file
import {File} from '@ionic-native/file/ngx';
constructor(private file: File) { }

...


async translateFilePath(file) {
 file = await this.file.resolveLocalFilesystemUrl(file);
 file = file.toInternalURL(); // 转化成cdvfile://地址
 return file;
}

处理图片

  1. 裁剪图片:裁剪图片有多种方式,可以通过 cordova-plugin-crop 插件进行图片简单的裁剪,但是用这个插件我遇到了比较头疼的问题,在Android上运行是完美的,但是在 IOS 系统上,通过crop插件得到的路径进行上传,接口死活接受不到文件,并且貌似跟Camera插件的quality也有关,查看了GitHub issue 也没有结果,如果不用Crop插件,上传文件是没有问题的,下面先附上单纯用FileTransfer插件上传的代码:
		// 这里的文件,是已经通过file插件,转换成了cdvfile:// 协议的
 		const fileTransfer: FileTransferObject = this.transfer.create();
        const options: FileUploadOptions = {
            fileKey: 'upload', // 后端接口接收文件的键名
            chunkedMode: false, // 这里一定要设置false, 否则安卓上是上传不成功的,
            fileName: file.substr(file.lastIndexOf('/') + 1),
            params: { // 接口额外的参数,视接口而定 }
        };
        try {
            const {response} = await fileTransfer.upload(file, encodeURI('http://demo.api/uploadFile'), options);
            const ret = JSON.parse(response);
            console.log(ret);
        } catch (e) {
            console.log(e);
        }
  1. 如果要用Crop插件,解决上面提到的IOS上传不成功的问题,则需要在Camera配置项当中加入 allowEdit : true
    这个方法不太可取,因为这个参数一旦开启,就会导致有两次裁剪图片,一次是Camera插件自身的裁剪,一次是Crop插件的裁剪,并且Camera插件的裁剪在IOS上表现是很垃圾的,,,所以我果断放弃了使用Crop插件
let cameraOption: any = {
     quality: 65,
     allowEdit: true, // IOS上,若是使用Crop插件裁剪图片,那么这个参数必须设置为true
     correctOrientation: true, // 旋转图像以在捕获过程中校正设备的方向
     destinationType: this.camera.DestinationType.FILE_URI,  // 返回的数据类型,默认DATA_URL
     sourceType: this.camera.PictureSourceType.SAVEDPHOTOALBUM,  // 来源类型,默认CAMERA
    };

最终解决方案:使用Javascript 插件 对图片进行裁剪

抛弃了Crop插件后,目光锁在了H5裁剪上,于是百度一番,找到了cropperjs 这个纯javascript插件,功能比较强大,原理使用Canvas进行图片编辑,对于浏览器兼容性较好。GitHub地址:https://github.com/fengyuanchen/cropperjs
在确认使用cropperjs插件后,图片上传,也是弃用了FileTransfer插件,官方也是不太建议使用FileTransfer上传的,并且也已经不再维护此插件,上传还是改用了ajax。

使用这种方式裁剪图片,需要解决一个问题,通过Camera插件获取的图片文件路径是file协议,把此路径直接放入img 的src 是不会显示图片的,那么就无法使用cropperjs进行裁剪,之前在cordova-plugin-file GitHub 上看到一段文字:

To use cdvfile as a tag' src you can convert it to native path via toURL() method of the resolved fileEntry, which you can get via resolveLocalFileSystemURL - see examples below.

You can also use cdvfile:// paths directly in the DOM, for example:


Note: This method requires following Content Security rules updates:

1. Add cdvfile: scheme to Content-Security-Policy meta tag of the index page, e.g.:
' data: gap: cdvfile: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
2. Add > to config.xml.

但是这个方法展示HTML图片,我也放弃了,因为第一个条件,就很麻烦,下面附上另一个解决方法:

// 借助ionic-webview提供的convertFileSrc方法转换file:// 协议的文件路径

import { WebView } from '@ionic-native/ionic-webview/ngx'; // 使用Cordova
import { DomSanitizer } from '@angular/platform-browser';

import { Capacitor } from '@capacitor/core'; // 若是使用Capacitor,则需要导入此
constructor(private webview: WebView, private domSanitizer: DomSanitizer) { }

...

let imgSrc = this.webview.convertFileSrc('file:///Users/dan/camera-image-12345.png'); // Cordova对应的
//  let imgSrc = Capacitor.convertFileSrc(filePath); // 使用Capacitor

imgSrc = this.domSanitizer.bypassSecurityTrustUrl(imgSrc); // 这样Dom中就可以正常显示file:// 协议的图片了

CropperJs 的使用

具体使用可以参照官方文档:https://github.com/fengyuanchen/cropperjs
当然也有大神对于Angular使用封装了一个插件:https://github.com/matheusdavidson/angular-cropperjs 其配置参数和官方文档无异

npm i angular-cropperjs --save
<angular-cropper [cropperOptions]="config" [imageUrl]="imageUrl" #angularCropper>angular-cropper>
import {NavController} from "@ionic/angular";
import {ActivatedRoute} from "@angular/router";
import { HttpClient} from "@angular/common/http";
import { DomSanitizer } from '@angular/platform-browser';
import {Component, OnInit, ViewChild} from '@angular/core';
import {ToolsService} from "../../services/tools/tools.service";
import {apilist} from "../../services/request/api/apiListConfig";
import {ComponentsService} from "../../services/component/component.service";

@Component({
  selector: 'app-crop',
  templateUrl: './crop.page.html',
  styleUrls: ['./crop.page.scss'],
})
export class CropPage implements OnInit {
  @ViewChild('angularCropper', { static: true}) angularCropper: any;
  cropper: any;
  imageData: any;
  config: any = {
    viewMode: 1,
    autoCropArea: 1,
    dragMode :'move',
    aspectRatio: this.activedRoute.snapshot.queryParamMap.get('imageAspectRatio'), //裁剪框的宽高比
    autoCrop:true, //初始化时,自动生成裁剪框 当初始化时,可以自动生成图像。(就是自动显示裁剪框,改成false裁剪框自动消失
    cropend: function () { // 每次裁剪完毕,获取裁剪后的图片
      this.getCropperImage();
    }.bind(this),
    ready: function () {
      this.getCropperImage(); // 初始化后,自动获取默认裁剪区域的图片
    }.bind(this)
  };
  imageUrl: any = "../../../assets/icon/[email protected]";

  constructor(
      private http: HttpClient,
      private dom: DomSanitizer,
      private toolsService: ToolsService,
      private navController: NavController,
      private activedRoute: ActivatedRoute,
      private componentsService: ComponentsService,
  ) { }

  async ngOnInit() {
    const localImage = this.activedRoute.snapshot.queryParamMap.get('imageSrc');
    if(localImage) this.imageUrl = this.dom.bypassSecurityTrustUrl(localImage) ;
  }

  getCropperImage() {
    this.imageData = this.angularCropper.cropper.getCroppedCanvas({
      width: 750, // 这里配置的是裁剪后,生成后的图片的大小,width和height至少需要设置一个,否则IOS可能无法生成裁剪图片导致上传文件是失败的,
      imageSmoothingEnabled: true,
      imageSmoothingQuality: 'high'
    });
  }

  async uploadCropImage() {
    await this.toolsService.showLoading();
    this.imageData.toBlob(blob => {  // this.imageData.toDataURL('image/jpeg') 可以生成base64, 这里是使用Blob来上传图片,推荐使用
      const formData = new FormData();
      formData.append('upload', blob, new Date().valueOf()+'.png');
      this.http.post(apilist().uploadFile, formData).toPromise().then((res: any) => {
        if(res.status) {
          this.toolsService.toast('上传成功');
          const events = this.activedRoute.snapshot.queryParamMap.get('events');
          this.componentsService.sendEvents({ type: events, data: res.data.url});
          this.navController.back();
        } else {
          this.toolsService.toast(res.msg);
        }
      }).catch(err => console.log(err)).finally(() => this.toolsService.hideLoading())
    })
  }
}

集成之后,Android和IOS都运行试过了,基本没有太大问题,尤其注意一点,如果IOS上出现裁剪框无法拖动,请试着将cropperjs安装至最新版本:

npm i cropperjs --save

过程中有问题请留言。

你可能感兴趣的:(ionic4/5)