2019独角兽企业重金招聘Python工程师标准>>> hot3.png

我们先看看linkface给开放的接口:

字段 类型 必需 描述
api_id string API 账户
api_secret string API 密钥
selfie_file file 见下方注释 需上传的图片文件 1,上传本地图片进行检测时选取此参数
selfie_url string 见下方注释 图片 1 的网络地址,采用抓取网络图片方式时需选取此参数
selfie_image_id file 见下方注释 图片 1 的id,在云端上传过图片可采用
historical_selfie_file file 见下方注释 需上传的图片文件 2,上传本地图片进行检测时选取此参数
historical_selfie_url string 见下方注释 图片 2 的网络地址,采用抓取网络图片方式时需选取此参数
historical_selfie_image_id string 见下方注释 图片 2 的id,在云端上传过图片可采用
selfie_auto_rotate boolean 值为 true 时,对图片 1 进行自动旋转。默认值为 false,不旋转
historical_selfie_auto_rotate boolean 值为 true 时,对图片 2 进行自动旋转。默认值为 false,不旋转

如文件所示,接口需要同时上传两个文件和两个字段,一般我们的web前端就很简单了,两个file类型的input组成的form提交就可以,若想实现文件的异步上传通俗的方式就是安装浏览器安全插件,或者就是使用form表单的提交target指向为iframe,然后将iframe隐藏,使用视窗的父子级调用完成,但是这仍需要我们使用form组件选择文件,很显然这样会使得我们的移动APP体验极差,我们期望的就是使用相机拍完照然后直接异步上传执行检测,当然我们可以使用XMLHTTPReauest2拼接一个formatdata上传

//不完全代码
let formData = new FormData();
formData.append('fileName',input.files[0]);
xhr.open("post", encodeURI(url));
xhr.send(formData);

但是,在web端,如果用户不使用input选择文件,我们是无法私自获取并上传文件的,这个浏览器的安全机制,想想如果可以拼接file://私自获取文件,我们还安全么?

那么针对于cordova plugin 就相当于我们浏览器的插件了,道理是一定的,通过js的方式调用底层接口。我们首先能够想得到的就是file-transfer这个插件,但是很遗憾的告诉你,这个插件一次只能上传一个文件, https://github.com/apache/cordova-plugin-file-transfer,

Parameters:fileURL: Filesystem URL representing the file on the device or a data URI. For backwards compatibility, this can also be the full path of the file on the device. (See Backwards Compatibility Notes below)server: URL of the server to receive the file, as encoded by encodeURI().successCallback: A callback that is passed a FileUploadResult object. (Function)errorCallback: A callback that executes if an error occurs retrieving the FileUploadResult. Invoked with a FileTransferError object. (Function)options: Optional parameters (Object). Valid keys:fileKey: The name of the form element. Defaults to file. (DOMString)
fileName: The file name to use when saving the file on the server. Defaults to image.jpg. (DOMString)
httpMethod: The HTTP method to use - either PUT or POST. Defaults to POST. (DOMString)
mimeType: The mime type of the data to upload. Defaults to image/jpeg. (DOMString)
params: A set of optional key/value pairs to pass in the HTTP request. (Object, key/value - DOMString)
chunkedMode: Whether to upload the data in chunked streaming mode. Defaults to true. (Boolean)
headers: A map of header name/header values. Use an array to specify more than one value. On iOS, FireOS, and Android, if a header named Content-Type is present, multipart form data will NOT be used. (Object)
trustAllHosts: Optional parameter, defaults to false. If set to true, it accepts all security certificates. This is useful since Android rejects self-signed security certificates. Not recommended for production use. Supported on Android and iOS. (boolean)

我真搞不懂既然cordova plugin封装,为啥不封装成文件数组接口呢,支持多文件和困难么?那么我们就来看看他的源码:

                    boolean multipartFormUpload = (headers == null) || !headers.has("Content-Type");if (multipartFormUpload) {conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);}// Set the cookies on the responseString cookie = getCookies(target);if (cookie != null) {conn.setRequestProperty("Cookie", cookie);}// Handle the other headersif (headers != null) {addHeadersToRequest(conn, headers);}/** Store the non-file portions of the multipart data as a string, so that we can add it* to the contentSize, since it is part of the body of the HTTP request.*/StringBuilder beforeData = new StringBuilder();try {for (Iterator<?> iter = params.keys(); iter.hasNext();) {Object key = iter.next();if(!String.valueOf(key).equals("headers")){beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append('"');beforeData.append(LINE_END).append(LINE_END);beforeData.append(params.getString(key.toString()));beforeData.append(LINE_END);}}} catch (JSONException e) {Log.e(LOG_TAG, e.getMessage(), e);}beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);beforeData.append("Content-Disposition: form-data; name=\"").append(fileKey).append("\";");beforeData.append(" filename=\"").append(fileName).append('"').append(LINE_END);beforeData.append("Content-Type: ").append(mimeType).append(LINE_END).append(LINE_END);byte[] beforeDataBytes = beforeData.toString().getBytes("UTF-8");byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END).getBytes("UTF-8");

看到了吗,它是拼接了报文,这就是能够解释它为啥还需要依赖 cordova-plugin-file这个插件了,它可以直接获取文件ArrayBuffer,很聪明啊,真的很聪明,为什么拼报文?岂不是很麻烦,正常我么使用java的http client是需要依赖 httpclient-4.0.1.jar commons-codec-1.3.jar  apache-mime4j-0.6.jar httpcore-4.0.1.jar httpmime-4.0.1.jar ,这无形之中就增大了app的大小,作为卡插拔式的插件,大小也是一个硬伤,所以封装插件的同学们学习吧,人家可不是盖的,拼接报文自然使得插件不需要依赖那些包了。

我们开脑补一下http报文协议:

一个HTTP请求报文由请求行(request line)、请求头部(header)、空行请求数据4个部分组成,下图给出了请求报文的一般格式。

所以按照标准拼写报文也是可以的。

但是我是一个H5工程师,我首先会使用H5技术去解决这件事,不然我就只能发挥java技能更改file-transfer这个插件了。XHR拼接formdata,可以是file也可以是一个blob,我曾将想过是不是有接口能够模拟封装input的file或者使用FileReader,然而还是那句话,浏览器为了安全不会让我们自己拼接file:// 的,但是cordova跨平台可以访问文件系统(你可以看一下 https://github.com/apache/cordova-plugin-file里http-equiv="Content-Security-Policy"相关的描述),毕竟我们开发的是移动app,这个功能是不可缺少的,我们使用cordova的file plugin还是可以获取文件的我们来看看ionic2提供的接口(http://ionicframework.com/docs/v2/native/file/  ):

readAsArrayBuffer(path, file)

Read file and return data as an ArrayBuffer.

Param Type Details
path string

Base FileSystem. Please refer to the iOS and Android filesystems above

file string

Name of file, relative to path.

Returns: Promise<ArrayBuffer|FileError> Returns a Promise that resolves with the contents of the file as ArrayBuffer or rejects with an error.

惊喜吧!有个这个我们就能够自己拼写blob类型的formdata了,话不多说我们直接上代码:

先写封装一个文件转换类,file-convert-util.ts:

import {File, FileError} from "ionic-native";
/**** @author 赵俊明*/export class FileConvertUtil {constructor() {}//讲文件转换为Blobpublic static convertFileToBlob(fullFilePath: string): Promise<Blob|FileError> {return new Promise((resolve, reject)=> {FileConvertUtil.convertFileToArrayBuffer(fullFilePath).then((arrayBuffer)=> {resolve(new Blob([arrayBuffer], {type: "image/" + FileConvertUtil.extractFileType(fullFilePath)}));}).catch((reason)=> {reject(reason);});});}//将文件装换为ArrayBufferpublic static convertFileToArrayBuffer(fullFilePath: string): Promise<ArrayBuffer | FileError> {return File.readAsArrayBuffer(FileConvertUtil.extractFilePath(fullFilePath), FileConvertUtil.extractFileName(fullFilePath));}//截取文件路径public static extractFilePath(fullFilePath: string): string {return fullFilePath.substr(0, fullFilePath.lastIndexOf('/'));}//截取文件名称public static extractFileName(fullFilePath: string): string {return fullFilePath.substr(fullFilePath.lastIndexOf('/') + 1);}//截取文件类型public static extractFileType(fullFilePath: string): string {return fullFilePath.split(".")[1];}}

基于XHR2的upload,xhr-multipart-upload.ts:

import {BrowserXhr} from "@angular/http";
import {FileConvertUtil} from "./file-convert-util";
import {FileError} from "ionic-native";
import {Injectable, Component} from "@angular/core";
/*** @author zhaojunming*/export class XHRMultipartFileUpload {private static browserXhr = new BrowserXhr();constructor() {}public static upload(url: string, files: {name: string,path: string}[], params: any): Promise<any> {const xhr = XHRMultipartFileUpload.browserXhr.build();xhr.open("post", encodeURI(url));let formData = new FormData();return new Promise((resolve, reject)=> {if (params) {for (let _v in params) {if (params.hasOwnProperty(_v)) {formData.append(_v, params[_v]);}}}let blobPromiseList: Array<Promise<Blob|FileError>> = [];files.forEach((file)=> {blobPromiseList.push(FileConvertUtil.convertFileToBlob(file.path));});Promise.all(blobPromiseList).then((result)=> {result.forEach((blob, index)=> {formData.append(files[index].name, blob, FileConvertUtil.extractFileName(files[index].path));});xhr.onreadystatechange = ()=> {if (xhr.readyState == 4) {if (xhr.status == 200) {resolve(JSON.parse(xhr.responseText));} else {reject({code: xhr.status, message: JSON.parse(xhr.responseText)});}}}xhr.send(formData);}).catch((reason)=> {reject(reason);});});}}

调用linkface的provider,linkface-verfication.ts:

/**** @author 赵俊明*/import {Injectable, Component} from "@angular/core";
import {XHRMultipartFileUpload} from "./xhr-multipart-upload";
import {Storage, LocalStorage} from "ionic-angular";//400 错误码对应信息
const ERROR_MAPPING = {"ENCODING_ERROR": "参数非UTF-8编码","DOWNLOAD_TIMEOUT": "网络地址图片获取超时","DOWNLOAD_ERROR": "网络地址图片获取失败","IMAGE_FILE_SIZE_TOO_BIG": "图片体积过大 ","IMAGE_ID_NOT_EXIST": "图片不存在","NO_FACE_DETECTED": "图片未检测出人脸 ","CORRUPT_IMAGE": "文件不是图片文件或已经损坏","INVALID_IMAGE_FORMAT_OR_SIZE": "图片大小或格式不符合要求","INVALID_ARGUMENT": "请求参数错误","UNAUTHORIZED": "账号或密钥错误","KEY_EXPIRED": "账号过期","RATE_LIMIT_EXCEEDED": "调用频率超出限额","NO_PERMISSION": "无调用权限","NOT_FOUND": "请求路径错误","INTERNAL_ERROR": "服务器内部错误"
};@Injectable()
export class LinkFaceVerfication {//普通照片比对URLprivate historicalSelfieVerificationURL = "https://v1-auth-api.visioncloudapi.com/identity/historical_selfie_verification";//公安水印照片与普通照片比对URLprivate selfieWatermarkVerificationURL = "https://v1-auth-api.visioncloudapi.com/identity/selfie_watermark_verification";private apiId: string;private apiSecret: string;//LocalStorageprivate storage = new Storage(LocalStorage);constructor() {this.getApiId().then(apiId=> {this.apiId = apiId || "6b666502c4324026b8604c8001a2cd14";}).catch(()=> {this.apiId = "6b666502c4324026b8604c8001a2cd14";});this.getApiSecret().then(apiSecret=> {this.apiSecret = apiSecret || "28cf8b8693e54d0b930d0a5089831841";}).catch(()=> {this.apiSecret = "28cf8b8693e54d0b930d0a5089831841";});}//普通照片比对public historicalSelfieVerification(selfie_file: string, historical_selfie_file: string, selfie_auto_rotate: boolean = true, historical_selfie_auto_rotate: boolean = true): Promise<any> {let params = {api_id: this.apiId,api_secret: this.apiSecret,selfie_auto_rotate: selfie_auto_rotate,historical_selfie_auto_rotate: historical_selfie_auto_rotate};let files = []files.push({name: "selfie_file", path: selfie_file});files.push({name: "historical_selfie_file", path: historical_selfie_file});return new Promise((resolve, reject)=> {XHRMultipartFileUpload.upload(this.historicalSelfieVerificationURL, files, params).then(result=> {resolve(result);}).catch(error=> {if (error && error.code == 400) {reject(ERROR_MAPPING[error.message.status]);} else {reject(JSON.stringify(error));}});});}//公安水印照片与普通照片比对public selfieWatermarkVerification(selfie_file: string, watermark_picture_file: string): Promise<any> {let params = {api_id: this.apiId, api_secret: this.apiSecret};let files = []files.push({name: "selfie_file", path: selfie_file});files.push({name: "watermark_picture_file", path: watermark_picture_file});return new Promise((resolve, reject)=> {XHRMultipartFileUpload.upload(this.selfieWatermarkVerificationURL, files, params).then(result=> {resolve(result);}).catch(error=> {if (error && error.code == 400) {reject(ERROR_MAPPING[error.message.status]);} else {reject(JSON.stringify(error));}});});}setApiId(apiId): boolean {if (apiId) {this.apiId = apiId;this.storage.set("apiId", apiId);return true;}return false;}setApiSecret(apiSecret): boolean {if (apiSecret) {this.apiSecret = apiSecret;this.storage.set("apiSecret", apiSecret);return true;}return false;}getApiId(): Promise<string> {return this.storage.get("apiId");}getApiSecret(): Promise<string> {return this.storage.get("apiSecret");}}

看看我们怎么调用:

this.linkFaceVerfication.historicalSelfieVerification(this.selfie_file, this.historical_selfie_file, true, true).then(result=> {this.confidence = (result.confidence * 100).toFixed(2);this.uploading = false;}).catch(reason=> {this.toastMessage(reason);this.uploading = false;});

我们来看看效果:

转载于:https://my.oschina.net/u/259422/blog/747399

在cordova中使用HTML5的多文件上传相关推荐

  1. HTML5实现大文件上传

    需求: 项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在20G内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以20G来进行限制. PC端全平台支持,要求支持Window ...

  2. php利用ajax文件上传,如何在PHP中利用AjaxForm实现一个文件上传功能

    如何在PHP中利用AjaxForm实现一个文件上传功能 发布时间:2020-12-18 14:52:38 来源:亿速云 阅读:94 作者:Leah 如何在PHP中利用AjaxForm实现一个文件上传功 ...

  3. winform中通过FileStream实现将文件上传

    winform中通过FileStream实现将文件上传 本实例实现功能:通过OpenFileDialog选择待上传的文件,并将所选文件的完整路径绑定到TreeView控间中显示,然后通过FolderB ...

  4. 在PHP中通过POST方法实现文件上传功能

    (本文内容属于转载,原文链接http://blog.sina.com.cn/s/blog_4fe447d501009rzh.html) 在PHP 5中通过POST方法实现文件上传非常简单,可以上传文本 ...

  5. [RFC1867] HTML中基于表单的文件上传

    网络工作组:E. Nebel 征求意见:1867 L. Masinter 类别:试验 施乐公司 十一月 1995 HTML中基于表单的文件上传 这个备忘录的状态 这个备忘录为互联网社区定义了一个试验协 ...

  6. html资源文件记载进度条,HTML5矢量实现文件上传进度条

    在HTML中,在文件上传的过程中,很多情况都是没有任何的提示,这在体验上很不好,用户都不知道到时有没有在上传.上传成功了没有,所以今天给大家介绍的内容是通过HT for Web矢量来实现HTML5文件 ...

  7. HTML5矢量实现文件上传进度条

    在HTML中,在文件上传的过程中,很多情况都是没有任何的提示,这在体验上很不好,用户都不知道到时有没有在上传.上传成功了没有,所以今天给大家介绍的内容是通过HT for Web矢量来实现HTML5文件 ...

  8. HTML5拖拽文件上传

    上传文件 HTML5新增了文件API,提供客户端本地操作文件的可能. 我们可以通过file表单或拖放操作选择文件,还可以通过JavaScript读取文件的名称.大小.类型.和修改时间. file类型的 ...

  9. vue+Element ui中使用 upload实现Excel文件上传

    文章目录 业务需求: 实现方式: 具体步骤: 1.引入组件: 2.data中定义: 3.methods中方法: 业务需求: 批量导入Excel文件 实现方式: 使用组件upload 具体步骤: 1.引 ...

最新文章

  1. mysql数据库c 操作o_linux下c操作mysql数据库。gcc报错。谁能帮忙看下啊。
  2. python caffe报错:No module named google
  3. 【持久化框架】SpringMVC+Spring4+Mybatis3集成,开发简单Web项目+源码下载【转】
  4. python多分支实现四则运算器代码_一份让Python疯狂加速的工具合集!
  5. 使用Docker启动Grafana环境
  6. android xml左边,Android XML – 如何将项目对齐到最左侧,中间和最右侧
  7. 一个简单的shell文件备份脚本
  8. nginx模块nginx_upstream_check_module来检查后端服务器的健康情况
  9. 深入理解 Nginx 之架构篇
  10. 锐捷长ping_锐捷实战系列(二) Wireshark抓包分析Ping过程
  11. Slat撒盐加密原理
  12. DICOMRT:XXX Target Volume,各种目标体积浅析
  13. 转转品牌升级,不只换了个LOGO
  14. 2021金昌市一中高考成绩查询,2021届甘肃省金昌市一中高三下学期高考一模考试数学(理)试卷参考答案.docx...
  15. 王道数据结构3.2.5——1、循环队列中设置tag区分头指针front和队尾指针rear相等时队列的状态是空还是满
  16. @hapi/joi 报错 “message“: “Cannot mix different versions of joi schemas“
  17. 雷电模拟器 + Xposed框架 + 北京消费券
  18. 黑马程序员---Java 容器集合
  19. java图片合成视频、音频及音频裁剪_MQ
  20. 华云大咖说 | 安超OS全面升级 最新亮点解密

热门文章

  1. 计算机语言由低到高就被分为了什么阶段,2004年4月高等教育自学考试认知心理试题...
  2. 鸿蒙测试机型微博,华为多款机型开启鸿蒙尝鲜:微博已适配HarmonyOS小尾巴
  3. 17 软件源_9成职场人支持“准点下班”,2020年度职场报告:工作是最大焦虑源
  4. php创建输入文本框,Asp:文本框与输入文本(PHP开发人员学习ASP)
  5. android文件读取工具类,Android 下读取Assets Properties操作封装工具类
  6. 转网口显示未识别的网络_已有1700万用户携号转网 超99%用户1小时内办结
  7. 2011年计算机一级考试题,2011年计算机一级考试试题及答案
  8. 《团队激励与沟通》第 7 讲——团队合作概述 重点部分总结
  9. nltk和python的关系_NLTK学习笔记(一):语言处理和Python
  10. java中父类与子类, 不同的两个类中的因为构造函数由于递归调用导致栈溢出问题...