在cordova中使用HTML5的多文件上传

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

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

字段类型必需描述
api_idstringAPI 账户
api_secretstringAPI 密钥
selfie_filefile见下方注释需上传的图片文件 1,上传本地图片进行检测时选取此参数
selfie_urlstring见下方注释图片 1 的网络地址,采用抓取网络图片方式时需选取此参数
selfie_image_idfile见下方注释图片 1 的id,在云端上传过图片可采用
historical_selfie_filefile见下方注释需上传的图片文件 2,上传本地图片进行检测时选取此参数
historical_selfie_urlstring见下方注释图片 2 的网络地址,采用抓取网络图片方式时需选取此参数
historical_selfie_image_idstring见下方注释图片 2 的id,在云端上传过图片可采用
selfie_auto_rotateboolean值为 true 时,对图片 1 进行自动旋转。默认值为 false,不旋转
historical_selfie_auto_rotateboolean值为 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.

ParamTypeDetails
pathstring

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

filestring

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/542560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python dataframe切片_python pandas dataframe 行列选择,切片操作方法

SQL中的select是根据列的名称来选取&#xff1b;Pandas则更为灵活&#xff0c;不但可根据列名称选取&#xff0c;还可以根据列所在的position&#xff08;数字&#xff0c;在第几行第几列&#xff0c;注意pandas行列的position是从0开始&#xff09;选取。相关函数如下&#xf…

php根据设备判断访问,PHP判断设备访问来源

/*** 判断用户请求设备是否是移动设备* return bool*/function isMobile() {//如果有HTTP_X_WAP_PROFILE则一定是移动设备if (isset($_SERVER[HTTP_X_WAP_PROFILE])) {return true;}//如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息if (isset($_SERVER[HTTP_VIA])…

机器学习 深度学习 ai_如何学习机器学习和人工智能?

机器学习 深度学习 aiSTRATEGY 战略 Learn theory practical aspects. 学习理论和实践方面的知识。 (At first get an overview of what you are going to learn). (首先获得要学习的内容的概述)。 Gain a good hold/insight on each concept. 掌握/理解每个概念。 If you …

linux常用命令和配置

2019独角兽企业重金招聘Python工程师标准>>> 启动php&#xff1a; /etc/init.d/php-fpm restart 查看PHP运行目录&#xff1a; which php /usr/bin/php 查看php-fpm进程数&#xff1a; ps aux | grep -c php-fpm 查看运行内存 /usr/bin/php -i|grep mem iptables如…

centos7时间同步_centos 8.x系统配置chrony时间同步服务

centos 8.x系统配置chrony时间同步服务CentOS 7.x默认使用的时间同步服务为ntp服务&#xff0c;但是CentOS 8开始在官方的仓库中移除了ntp软件&#xff0c;换成默认的chrony进行时间同步的服务&#xff0c;chrony既可以作为客户端向其他时间服务器发送时间同步请求&#xff0c;…

php可以用scanf,C/C++中 使用scanf和printf如何读入输出double型数据。

黄舟2017-04-17 13:47:232楼注意scanf函数和printf函数是不同寻常的函数&#xff0c;因为它们都没有将函数的参数限制为固定数量。scanf函数和printf函数又可变长度的参数列表。当调用带可变长度参数列表的函数时&#xff0c;编译器会安排float参数自动转换成为double类型&…

ICWAI和ICWA的完整形式是什么?

ICWAI / ICWA&#xff1a;印度成本与工程会计师协会/印度儿童福利法 (ICWAI / ICWA: Institute of Cost and Works Accountants of India / Indian Child Welfare Act) 1)ICWAI&#xff1a;印度成本与工程会计师协会 (1) ICWAI: Institute of Cost and Works Accountants of In…

深入研究java.lang.Runtime类【转】

转自&#xff1a;http://blog.csdn.net/lastsweetop/article/details/3961911 目录(?)[-] javalang 类 RuntimegetRuntimeexitaddShutdownHookremoveShutdownHookhaltrunFinalizersOnExitexecexecexecexecexecexecavailableProcessorsfreeMemorytotalMemorymaxMemorygcrunFina…

java队列实现限流,java中应对高并发的两种策略

目的&#xff1a;提高可用性通过ExecutorService实现队列泄洪//含有20个线程的线程池private ExecutorService executorService Executors.newFixedThreadPool(20);将有并发压力的下游代码放入到线程池的submit方法中&#xff0c;如下&#xff1a;//同步调用线程池的submit方法…

crontab 日志_liunx 中定时清理过期日志文件

问题描述经常遇到日志文件过多&#xff0c;占用大量磁盘空间&#xff0c;需要定期删除过期日志。问题涉及方面删除过期日志的脚本。定时任务删除任务脚本先查询到过期的日志文件&#xff0c;然后删除。语法find path -option [ -print ] [ -exec -ok command ] …

JavaScript | 数组的常用属性和方法

JavaScript的通用属性和数组方法 (Common properties and methods of array in JavaScript ) Properties/MethodsDescriptionsarray.lengthReturns the length of the array/total number of elements of the array array[index]Returns the item name stored at “index” pos…

php dbutils 使用,dbutilsapi

相对lisp?而?言,可以使?用.net的强?大api,实现各种酷炫功能。相对c#及arx?...文件utils.py的模块名分别是mycompany.utils和 mycompany.web.utils。 mycompany......CouchDB -b 关闭后台运行的 CouchDB : CouchDB -d web 访问:http://127.0.0.1:5984/_utils/index.html E-…

html 导航栏

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>lvnian学习(http://lvnian.blog.51cto.com/)</title> <style> ul {list-style-type:none;margin:0;padding:0; }a:link,a:visited{display:block;font-weigh…

将搜索二叉树转换为链表_将给定的二叉树转换为双链表(DLL)

将搜索二叉树转换为链表Given a Binary tree and we have to convert it to a Doubly Linked List (DLL). 给定二叉树&#xff0c;我们必须将其转换为双链表(DLL)。 Algorithm: 算法&#xff1a; To solve the problem we can follow this algorithm: 为了解决这个问题&#…

cuda编程_CUDA刷新器:CUDA编程模型

CUDA刷新器&#xff1a;CUDA编程模型 CUDA Refresher: The CUDA Programming Model CUDA&#xff0c;CUDA刷新器&#xff0c;并行编程 这是CUDA更新系列的第四篇文章&#xff0c;它的目标是刷新CUDA中的关键概念、工具和初级或中级开发人员的优化。 CUDA编程模型提供了GPU体系结…

php curl_error源码,PHP curl_error函数

PHP curl_error函数(PHP 4 > 4.0.3, PHP 5)curl_error — 返回一个保护当前会话最近一次错误的字符串说明string curl_error ( resource $ch )返回一条最近一次cURL操作明确的文本的错误信息。参数ch由 curl_init() 返回的 cURL 句柄。返回值返回错误信息或 (空字符串) 如果…

SQL中Where与Having的区别

“Where” 是一个约束声明&#xff0c;使用Where来约束来之数据库的数据&#xff0c;Where是在结果返回之前起作用的&#xff0c;且Where中不能使用聚合函数。“Having”是一个过滤声明&#xff0c;是在查询返回结果集以后对查询结果进行的过滤操作&#xff0c;在Having中可以使…

java 逻辑表达式 布尔_使用基本逻辑门实现布尔表达式

java 逻辑表达式 布尔将布尔表达式转换为逻辑电路 (Converting Boolean Expression to Logic Circuit) The simplest way to convert a Boolean expression into a logical circuit is to follow the reverse approach in which we start from the output of the Boolean expre…

python自然语言处理书籍_精通Python自然语言处理pdf

自然语言处理&#xff08;NLP&#xff09;是有关计算语言学与人工智能的研究领域之一。NLP主要关注人机交互&#xff0c;它提供了计算机和人类之间的无缝交互&#xff0c;使得计算机在机器学习的帮助下理解人类语言。 本书详细介绍如何使用Python执行各种自然语言处理&#xff…

通达oa 2013 php解密,通达OA漏洞学习 - 安全先师的个人空间 - OSCHINA - 中文开源技术交流社区...

说明通达OA漏洞在去年上半年已爆出&#xff0c;这不趁着周末没事做&#xff0c;将源码下载下来进行复现学习。文件包含测试文件包含检测&#xff0c;payload1:ip/ispirit/interface/gateway.php?json{"url":"/general/../../mysql5/my.ini"}利用文件包含访…