在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.pswp.cn/news/542560.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/542560.shtml
英文地址,請注明出處:http://en.pswp.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"}利用文件包含訪…