filecache
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
import Dns from 'okhttp3.Dns';
|
||||
import UnknownHostException from 'java.net.UnknownHostException';
|
||||
import InetAddress from 'java.net.InetAddress';
|
||||
import Inet4Address from 'java.net.Inet4Address';
|
||||
|
||||
export class OKDns implements Dns {
|
||||
|
||||
public override lookup(hostName: string): kotlin.collections.MutableList<InetAddress> {
|
||||
if (hostName == null) {
|
||||
throw UnknownHostException("hostname == null");
|
||||
} else {
|
||||
try {
|
||||
let inetAddressesList: Array<InetAddress> = [];
|
||||
let inetAddresses = InetAddress.getAllByName(hostName);
|
||||
for (inetAddress in inetAddresses) {
|
||||
if (inetAddress instanceof Inet4Address) {
|
||||
inetAddressesList.unshift(inetAddress)
|
||||
} else {
|
||||
inetAddressesList.push(inetAddress);
|
||||
}
|
||||
}
|
||||
return inetAddressesList;
|
||||
} catch (e: Exception) {
|
||||
let unknownHostException = new UnknownHostException("error");
|
||||
unknownHostException.initCause(e);
|
||||
throw unknownHostException;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
|
||||
|
||||
class StatusCode {
|
||||
public static statusCodeMap : Map<string, string> | null = null;
|
||||
|
||||
private static initStatusCodeMap() {
|
||||
let map = new Map<string, string>();
|
||||
this.statusCodeMap = map;
|
||||
map.set('100', "Continue");
|
||||
map.set('101', "Switching Protocol");
|
||||
map.set('200', "OK");
|
||||
map.set('201', "Created");
|
||||
map.set('202', "Accepted");
|
||||
map.set('203', "Non-Authoritative Information");
|
||||
map.set('204', "No Content");
|
||||
map.set('205', "Reset Content");
|
||||
map.set('206', "Partial Content");
|
||||
map.set('300', "Multiple Choice");
|
||||
map.set('301', "Moved Permanently");
|
||||
map.set('302', "Found");
|
||||
map.set('303', "See Other");
|
||||
map.set('304', "Not Modified");
|
||||
map.set('305', "Use Proxy");
|
||||
map.set('306', "unused");
|
||||
map.set('307', "Temporary Redirect");
|
||||
map.set('308', "Permanent Redirect");
|
||||
map.set('400', "Bad Request");
|
||||
map.set('401', "Unauthorized");
|
||||
map.set('402', "Payment Required");
|
||||
map.set('403', "Forbidden");
|
||||
map.set('404', "Not Found");
|
||||
map.set('405', "Method Not Allowed");
|
||||
map.set('406', "Not Acceptable");
|
||||
map.set('407', "Proxy Authentication Required");
|
||||
map.set('408', "Request Timeout");
|
||||
map.set('409', "Conflict");
|
||||
map.set('410', "Gone");
|
||||
map.set('411', "Length Required");
|
||||
map.set('412', "Precondition Failed");
|
||||
map.set('413', "Payload Too Large");
|
||||
map.set('414', "URI Too Long");
|
||||
map.set('415', "Unsupported Media Type");
|
||||
map.set('416', "Requested Range Not Satisfiable");
|
||||
map.set('417', "Expectation Failed");
|
||||
map.set('418', "I'm a teapot");
|
||||
map.set('421', "Misdirected Request");
|
||||
map.set('426', "Upgrade Required");
|
||||
map.set('428', "Precondition Required");
|
||||
map.set('429', "Too Many Requests");
|
||||
map.set('431', "Request Header Fields Too Large");
|
||||
map.set('500', "Internal Server Error");
|
||||
map.set('501', "Not Implemented");
|
||||
map.set('502', "Bad Gateway");
|
||||
map.set('503', "Service Unavailable");
|
||||
map.set('504', "Gateway Timeout");
|
||||
map.set('505', "HTTP Version Not Supported");
|
||||
map.set('506', "Variant Also Negotiates");
|
||||
map.set('507', "Variant Also Negotiates");
|
||||
map.set('511', "Network Authentication Required");
|
||||
}
|
||||
|
||||
public static getStatus(code : string) : string {
|
||||
let map = this.statusCodeMap;
|
||||
if (map == null) {
|
||||
this.initStatusCodeMap();
|
||||
}
|
||||
let tmp = this.statusCodeMap!;
|
||||
if (!(tmp.has(code))) {
|
||||
return 'unknown status';
|
||||
} else {
|
||||
return tmp.get(code)! as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
StatusCode
|
||||
}
|
||||
+507
@@ -0,0 +1,507 @@
|
||||
import { DownloadFileOptions, DownloadTask, DownloadFileProgressUpdateCallback, OnProgressDownloadResult } from '../../../interface.uts';
|
||||
import { NetworkDownloadFileListener } from '../NetworkManager.uts'
|
||||
import OkHttpClient from 'okhttp3.OkHttpClient';
|
||||
import TimeUnit from 'java.util.concurrent.TimeUnit';
|
||||
import ExecutorService from 'java.util.concurrent.ExecutorService';
|
||||
import Executors from 'java.util.concurrent.Executors';
|
||||
import Dispatcher from 'okhttp3.Dispatcher';
|
||||
import Callback from 'okhttp3.Callback';
|
||||
import Response from 'okhttp3.Response';
|
||||
import Request from 'okhttp3.Request';
|
||||
import Call from 'okhttp3.Call';
|
||||
import IOException from 'java.io.IOException';
|
||||
import ResponseBody from 'okhttp3.ResponseBody';
|
||||
import File from 'java.io.File';
|
||||
import BufferedSink from 'okio.BufferedSink';
|
||||
import BufferedSource from 'okio.BufferedSource';
|
||||
import Okio from 'okio.Okio';
|
||||
import TextUtils from 'android.text.TextUtils';
|
||||
import StringTokenizer from 'java.util.StringTokenizer';
|
||||
import MimeTypeMap from 'android.webkit.MimeTypeMap';
|
||||
import URLDecoder from 'java.net.URLDecoder';
|
||||
import CookieManager from 'android.webkit.CookieManager';
|
||||
import KotlinArray from 'kotlin.Array';
|
||||
import Context from 'android.content.Context';
|
||||
import Environment from 'android.os.Environment';
|
||||
import { CookieInterceptor } from '../interceptor/CookieInterceptor.uts'
|
||||
|
||||
class NetworkDownloadTaskImpl implements DownloadTask {
|
||||
private call : Call | null = null;
|
||||
private listener : NetworkDownloadFileListener | null = null;
|
||||
constructor(call : Call | null, listener : NetworkDownloadFileListener) {
|
||||
this.call = call;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public abort() {
|
||||
if (this.call != null) {
|
||||
this.call?.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public onProgressUpdate(option : DownloadFileProgressUpdateCallback) {
|
||||
const kListener = this.listener;
|
||||
if (kListener != null) {
|
||||
kListener.progressListeners.add(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class DownloadController {
|
||||
private static instance : DownloadController | null = null
|
||||
|
||||
/**
|
||||
* 上传的线程池
|
||||
*/
|
||||
private downloadExecutorService : ExecutorService | null = null;
|
||||
|
||||
public static getInstance() : DownloadController {
|
||||
if (this.instance == null) {
|
||||
this.instance = new DownloadController();
|
||||
}
|
||||
return this.instance!;
|
||||
}
|
||||
|
||||
public downloadFile(options : DownloadFileOptions, listener : NetworkDownloadFileListener) : DownloadTask {
|
||||
const client = this.createDownloadClient(options);
|
||||
let request = this.createDownloadRequest(options, listener);
|
||||
if (request == null) {
|
||||
return new NetworkDownloadTaskImpl(null, listener);
|
||||
}
|
||||
let call : Call = client.newCall(request);
|
||||
call.enqueue(new SimpleDownloadCallback(listener, options.filePath ?? ""));
|
||||
|
||||
let task = new NetworkDownloadTaskImpl(call, listener);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private createDownloadClient(option : DownloadFileOptions) : OkHttpClient {
|
||||
|
||||
let clientBuilder = OkHttpClient.Builder();
|
||||
const timeout : Long = option.timeout != null ? option.timeout!.toLong() : 120000;
|
||||
clientBuilder.connectTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
clientBuilder.readTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
clientBuilder.writeTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
clientBuilder.addInterceptor(new CookieInterceptor());
|
||||
|
||||
if (this.downloadExecutorService == null) {
|
||||
this.downloadExecutorService = Executors.newFixedThreadPool(10);
|
||||
}
|
||||
|
||||
clientBuilder.dispatcher(new Dispatcher(this.downloadExecutorService));
|
||||
|
||||
return clientBuilder.build();
|
||||
}
|
||||
|
||||
private createDownloadRequest(options : DownloadFileOptions, listener : NetworkDownloadFileListener) : Request | null {
|
||||
let requestBilder = new Request.Builder();
|
||||
try {
|
||||
requestBilder.url(options.url);
|
||||
} catch (exception : Exception) {
|
||||
let option: UTSJSONObject = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '-1';
|
||||
option['errorMsg'] = "invalid URL";
|
||||
let cause = exception.cause.toString();
|
||||
option['cause'] = new SourceError(cause);
|
||||
if (listener != null) {
|
||||
listener.onComplete(option);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let ua = UTSAndroid.getWebViewInfo(UTSAndroid.getAppContext()!)["ua"].toString();
|
||||
requestBilder.header("User-Agent", ua);
|
||||
|
||||
const headers = options.header?.toMap();
|
||||
if (headers != null) {
|
||||
for (entry in headers) {
|
||||
const key = entry.key;
|
||||
const value = entry.value;
|
||||
|
||||
if (value != null) {
|
||||
requestBilder.addHeader(key, "" + value);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return requestBilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleDownloadCallback implements Callback {
|
||||
private downloadFilePath = "/uni-download/";
|
||||
private listener : NetworkDownloadFileListener | null = null;
|
||||
private specifyPath = "";
|
||||
constructor(listener : NetworkDownloadFileListener, specifyPath : string) {
|
||||
this.listener = listener;
|
||||
if(specifyPath.startsWith("unifile://")){
|
||||
this.specifyPath = UTSAndroid.convert2AbsFullPath(specifyPath);
|
||||
}else{
|
||||
this.specifyPath = specifyPath
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override onFailure(call : Call, exception : IOException) {
|
||||
let option: UTSJSONObject = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '-1';
|
||||
option['errorMsg'] = exception.message;
|
||||
let cause = exception.cause.toString();
|
||||
option['cause'] = new SourceError(cause);
|
||||
this.listener?.onComplete(option);
|
||||
}
|
||||
override onResponse(call : Call, response : Response) {
|
||||
if (response.isSuccessful()) {
|
||||
|
||||
const source = response.body()?.source()
|
||||
if (source != null) {
|
||||
let mime_type = response.body()?.contentType();
|
||||
let extension = "data";
|
||||
if (mime_type != null) {
|
||||
let mime_type1 = mime_type.toString(); // 例如: "application/json; charset=utf-8"
|
||||
extension = mime_type.subtype(); // 子类型,例如: "json"
|
||||
}
|
||||
const tempFile = this.getTempFile()
|
||||
let tempSink : BufferedSink | null = null;
|
||||
let tempSource : BufferedSource | null = null;
|
||||
let targetSink : BufferedSink | null = null;
|
||||
try {
|
||||
tempSink = Okio.buffer(Okio.sink(tempFile));
|
||||
let totalBytesRead : Long = 0;
|
||||
const contentLength = response.body()!!.contentLength();
|
||||
const bufferSize : Int = 8 * 1024;
|
||||
const buffer = ByteArray(bufferSize);
|
||||
|
||||
|
||||
do {
|
||||
let bytesRead = source.read(buffer);
|
||||
if (bytesRead == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
tempSink.write(buffer, 0, bytesRead)
|
||||
totalBytesRead += bytesRead.toLong();
|
||||
const progress = (totalBytesRead.toFloat() / contentLength) * 100
|
||||
let downloadProgressUpdate : OnProgressDownloadResult = {
|
||||
progress: progress,
|
||||
totalBytesWritten: totalBytesRead,
|
||||
totalBytesExpectedToWrite: contentLength
|
||||
}
|
||||
this.listener?.onProgress(downloadProgressUpdate);
|
||||
|
||||
} while (true)
|
||||
|
||||
tempSink.flush()
|
||||
|
||||
tempSource = Okio.buffer(Okio.source(tempFile));
|
||||
this.specifyPath = this.specifyPath.replace('{{ext}}',extension);
|
||||
let targetFile = this.getFile(response)
|
||||
targetSink = Okio.buffer(Okio.sink(targetFile));
|
||||
targetSink.writeAll(tempSource);
|
||||
targetSink.flush();
|
||||
|
||||
let option = {};
|
||||
option['statusCode'] = response.code() + "";
|
||||
if (targetFile.exists()) {
|
||||
option['tempFilePath'] = targetFile.getPath();
|
||||
}
|
||||
this.listener?.onComplete(option);
|
||||
} catch (e: Exception){
|
||||
let option: UTSJSONObject = {};
|
||||
const code = "-1"
|
||||
const errorMsg = e.message
|
||||
option['statusCode'] = code;
|
||||
option['errorCode'] = code;
|
||||
option['errorMsg'] = errorMsg;
|
||||
const sourceError = new SourceError(errorMsg ?? "");
|
||||
option['cause'] = sourceError;
|
||||
this.listener?.onComplete(option);
|
||||
} finally {
|
||||
tempSink?.close()
|
||||
targetSink?.close()
|
||||
tempSource?.close()
|
||||
source?.close()
|
||||
tempFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
let option: UTSJSONObject = {};
|
||||
const code = response.code() + "";
|
||||
const errorMsg = response.body()?.string();
|
||||
option['statusCode'] = code;
|
||||
option['errorCode'] = code;
|
||||
option['errorMsg'] = errorMsg;
|
||||
const sourceError = new SourceError(errorMsg ?? "");
|
||||
sourceError.code = response.code();
|
||||
option['cause'] = sourceError;
|
||||
this.listener?.onComplete(option);
|
||||
}
|
||||
}
|
||||
//ext:string
|
||||
getTempFile() : File {
|
||||
return new File(UTSAndroid.getAppContext()!.getExternalCacheDir(), "temp_" + System.currentTimeMillis())
|
||||
}
|
||||
|
||||
|
||||
getRealPath() : string {
|
||||
var path = UTSAndroid.getAppTempPath() ?? "";
|
||||
return path + this.downloadFilePath;
|
||||
}
|
||||
|
||||
getFile(response : Response) : File {
|
||||
let targetPath = "";
|
||||
if (this.specifyPath != "") {
|
||||
const sourcePath = UTSAndroid.convert2AbsFullPath("/");
|
||||
const sourceFileDir = new File(sourcePath);
|
||||
if (this.isDescendant(sourceFileDir, new File(this.specifyPath))) {
|
||||
let option = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '602001';
|
||||
option['errorMsg'] = "This path is not supported";
|
||||
option['cause'] = null;
|
||||
this.listener?.onComplete(option);
|
||||
return new File("")
|
||||
}
|
||||
|
||||
const pos = this.specifyPath.lastIndexOf("/")
|
||||
if (pos == this.specifyPath.length - 1) {
|
||||
//如果filePath是目录
|
||||
if (this.isAbsolute(this.specifyPath)) {
|
||||
targetPath = this.specifyPath;
|
||||
} else {
|
||||
targetPath = UTSAndroid.getAppTempPath()!! + "/" + this.specifyPath
|
||||
}
|
||||
} else {
|
||||
let path = "";
|
||||
if (this.isAbsolute(this.specifyPath)) {
|
||||
path = this.specifyPath;
|
||||
} else {
|
||||
path = UTSAndroid.getAppTempPath()!! + "/" + this.specifyPath;
|
||||
}
|
||||
|
||||
var file = new File(path)
|
||||
const parentFile = file.getParentFile()
|
||||
if (parentFile != null) {
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs()
|
||||
}
|
||||
}
|
||||
if (file.exists() && file.isDirectory()) {
|
||||
let option = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '602001';
|
||||
option['errorMsg'] = "The target file path is already a directory file, and file creation failed.";
|
||||
option['cause'] = null;
|
||||
this.listener?.onComplete(option);
|
||||
}
|
||||
|
||||
if (file.exists()) {
|
||||
const index = path.lastIndexOf(".");
|
||||
let tFileName = path;
|
||||
let tFileType = "";
|
||||
if (index >= 0) {
|
||||
tFileName = path.substring(0, index)
|
||||
tFileType = path.substring(index)
|
||||
}
|
||||
|
||||
var number = 1
|
||||
while (new File(path).exists()) {
|
||||
path = tFileName + "(" + number + ")" + tFileType;
|
||||
number++
|
||||
}
|
||||
file = new File(path)
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
file.createNewFile()
|
||||
} catch (exception : Exception) {
|
||||
let option = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '602001';
|
||||
option['errorMsg'] = exception.message;
|
||||
let cause = exception.cause.toString();
|
||||
option['cause'] = new SourceError(cause);
|
||||
this.listener?.onComplete(option);
|
||||
}
|
||||
}
|
||||
return file
|
||||
}
|
||||
} else {
|
||||
targetPath = this.getRealPath();
|
||||
}
|
||||
|
||||
|
||||
let fileName = "";
|
||||
let remoteFileName = response.header("content-disposition");
|
||||
if (!TextUtils.isEmpty(remoteFileName)) {
|
||||
// form-data; name="file"; filename="xxx.pdf"
|
||||
const segments : KotlinArray<String | null> | null = this.stringSplit(remoteFileName, ";")
|
||||
if (segments != null) {
|
||||
for (let i : Int = 0; i < segments.size; i++) {
|
||||
const segment = segments[i];
|
||||
if (segment != null) {
|
||||
if (segment.contains("filename")) {
|
||||
const pair = this.stringSplit(segment.trim(), "=") //目前认为只存在一个键值对
|
||||
if (pair != null && pair.size > 1) {
|
||||
let key = pair[0];
|
||||
let value = pair[1];
|
||||
const reg = new RegExp("^\"|\"$","g")
|
||||
if (key != null) {
|
||||
key = key.replace(reg, "");
|
||||
}
|
||||
if (value != null) {
|
||||
value = value.replace(reg, "");
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value) && key!.equals("filename", true)) {
|
||||
if (value != null) {
|
||||
fileName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(fileName)) {
|
||||
let path = response.request().url().encodedPath()
|
||||
let pos = path.lastIndexOf('/')
|
||||
if (pos >= 0) {
|
||||
path = path.substring(pos + 1)
|
||||
if (path.indexOf('.') >= 0 || path.length > 0) { //存在类型后缀或者没有文件格式后缀的情况,取最后LastPathComponent的名称当做文件名。
|
||||
if (path.contains("?")) {
|
||||
path = path.substring(0, path.indexOf("?"))
|
||||
}
|
||||
fileName = path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(fileName)) {
|
||||
fileName = System.currentTimeMillis().toString()
|
||||
const contentType = response.header("content-type")
|
||||
let type = MimeTypeMap.getSingleton().getExtensionFromMimeType(contentType);
|
||||
if (type != null) {
|
||||
fileName += "." + type;
|
||||
}
|
||||
}
|
||||
|
||||
fileName = URLDecoder.decode(fileName, "UTF-8")
|
||||
fileName = fileName.replace(File.separator.toRegex(), "")
|
||||
if (fileName.contains("?")) {
|
||||
fileName = fileName.replace("\\?".toRegex(), "0")
|
||||
}
|
||||
if (fileName.length > 80) {
|
||||
const subFileName : String = fileName.substring(0, 80)
|
||||
fileName = subFileName + System.currentTimeMillis()
|
||||
}
|
||||
|
||||
|
||||
if (new File(targetPath + fileName).exists()) {
|
||||
const index = fileName.lastIndexOf(".");
|
||||
let tFileName = fileName;
|
||||
let tFileType = "";
|
||||
if (index >= 0) {
|
||||
tFileName = fileName.substring(0, index)
|
||||
tFileType = fileName.substring(index)
|
||||
|
||||
//fileName是 .xxx的情况
|
||||
if(tFileName == ""){
|
||||
tFileName = tFileType
|
||||
tFileType = ""
|
||||
}
|
||||
} else {
|
||||
tFileName = fileName
|
||||
}
|
||||
var number = 1
|
||||
while (new File(targetPath + fileName).exists()) {
|
||||
fileName = tFileName + "(" + number + ")" + tFileType;
|
||||
number++
|
||||
}
|
||||
}
|
||||
|
||||
targetPath += fileName
|
||||
|
||||
const file = new File(targetPath)
|
||||
const parentFile = file.getParentFile()
|
||||
if (parentFile != null) {
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs()
|
||||
}
|
||||
}
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
file.createNewFile()
|
||||
} catch (exception : Exception) {
|
||||
let option: UTSJSONObject = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '602001';
|
||||
option['errorMsg'] = exception.message;
|
||||
let cause = exception.cause.toString();
|
||||
option['cause'] = new SourceError(cause);
|
||||
this.listener?.onComplete(option);
|
||||
}
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
isAbsolute(path : string) : boolean {
|
||||
const context = UTSAndroid.getAppContext()!! as Context;
|
||||
if (path.startsWith(context.getFilesDir().getParent())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const exPath = context.getExternalFilesDir(null)?.getParent();
|
||||
if (exPath != null && path.startsWith(exPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个文件的上下级关系
|
||||
*/
|
||||
isDescendant(parent : File, child : File) : boolean {
|
||||
//有可能开发者传入的是/sdcard 或者/storage/emulated/ 这样的文件路径, 所以要用软连接的实际文件路径进行对比.
|
||||
if (child.getCanonicalPath() == parent.getCanonicalPath()) {
|
||||
return true;
|
||||
}
|
||||
let parentFile = child.getParentFile();
|
||||
|
||||
if (parentFile == null) {
|
||||
return false;
|
||||
}
|
||||
return this.isDescendant(parent, parentFile);
|
||||
}
|
||||
|
||||
|
||||
stringSplit(str : String | null, delim : String | null) : KotlinArray<String | null> | null {
|
||||
if (!TextUtils.isEmpty(str) && !TextUtils.isEmpty(delim)) {
|
||||
const stringTokenizer = new StringTokenizer(str, delim, false);
|
||||
const result = arrayOfNulls<String>(stringTokenizer.countTokens())
|
||||
var index : Int = 0
|
||||
while (stringTokenizer.hasMoreElements()) {
|
||||
result[index] = stringTokenizer.nextToken().trim()
|
||||
index += 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
import Interceptor from 'okhttp3.Interceptor';
|
||||
import Response from 'okhttp3.Response';
|
||||
import CookieHandler from 'java.net.CookieHandler';
|
||||
import TreeMap from 'java.util.TreeMap';
|
||||
import Headers from 'okhttp3.Headers';
|
||||
import Request from 'okhttp3.Request';
|
||||
class CookieInterceptor implements Interceptor {
|
||||
|
||||
override intercept(chain : Interceptor.Chain) : Response {
|
||||
let request = chain.request()
|
||||
let headerCookie = request.header("cookie")
|
||||
let uri = request.url().uri()
|
||||
let cookieHandler = CookieHandler.getDefault()
|
||||
if (headerCookie == null) {
|
||||
let requestBuilder = request.newBuilder()
|
||||
try {
|
||||
let currentHeaders = this.toMap(request.headers())
|
||||
let localCookie = cookieHandler.get(uri, currentHeaders)
|
||||
this.addCookies(requestBuilder, localCookie)
|
||||
} catch (e : Exception) {
|
||||
}
|
||||
request = requestBuilder.build()
|
||||
}
|
||||
|
||||
let response = chain.proceed(request)
|
||||
|
||||
try {
|
||||
cookieHandler.put(uri, this.toMap(response.headers()))
|
||||
} catch (e : Exception) {
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
private toMap(headers : Headers) : MutableMap<String, MutableList<String>> {
|
||||
let result : MutableMap<String, MutableList<String>> = new TreeMap(String.CASE_INSENSITIVE_ORDER)
|
||||
let size = headers.size()
|
||||
for (let i:Int = 0; i < size; i++) {
|
||||
let name = headers.name(i)
|
||||
let values = result[name]
|
||||
if (values == null) {
|
||||
values = arrayListOf()
|
||||
result[name] = values
|
||||
}
|
||||
values.add(headers.value(i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private addCookies(builder : Request.Builder, localCookie : MutableMap<String, MutableList<String>>) : void {
|
||||
|
||||
let totalList = mutableListOf<String>()
|
||||
let flagList = mutableListOf<String>()
|
||||
for (key in localCookie.keys) {
|
||||
if (flagList.size == 2) {
|
||||
break
|
||||
}
|
||||
if ("cookie".equals(key, true) || "cookie2".equals(key, true)) {
|
||||
flagList.add(key)
|
||||
let cookieList = localCookie[key]
|
||||
if (!cookieList.isNullOrEmpty()) {
|
||||
totalList.addAll(cookieList)
|
||||
}
|
||||
}
|
||||
}
|
||||
let headerStr = new StringBuilder()
|
||||
for (let str in totalList) {
|
||||
headerStr.append(str)
|
||||
headerStr.append("; ")
|
||||
}
|
||||
if (headerStr.toString().endsWith("; ")) {
|
||||
headerStr.deleteRange(headerStr.length - 2, headerStr.length - 1)
|
||||
}
|
||||
if (!headerStr.toString().isEmpty()){
|
||||
builder.addHeader("Cookie", headerStr.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
CookieInterceptor
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import Arrays from 'java.util.Arrays';
|
||||
import KotlinArray from 'kotlin.Array'
|
||||
|
||||
class SSLConfig {
|
||||
|
||||
private keystore ?: string = null;
|
||||
private storePass ?: string = null;
|
||||
private ca ?: KotlinArray<String> = null;
|
||||
|
||||
public getKeystore() : string | null {
|
||||
return this.keystore;
|
||||
}
|
||||
|
||||
public setKeystore(ks : string) {
|
||||
if (ks == null) {
|
||||
ks = "";
|
||||
}
|
||||
this.keystore = ks;
|
||||
}
|
||||
|
||||
public getStorePass() : string | null {
|
||||
return this.storePass;
|
||||
}
|
||||
|
||||
public setStorePass(sp : string) {
|
||||
if (sp == null) {
|
||||
sp = "";
|
||||
}
|
||||
this.storePass = sp;
|
||||
}
|
||||
|
||||
|
||||
public getCa() : KotlinArray<String> | null {
|
||||
return this.ca;
|
||||
}
|
||||
|
||||
public setCa(ca : KotlinArray<String>) {
|
||||
if (ca == null) {
|
||||
ca = emptyArray();
|
||||
}
|
||||
this.ca = ca;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
SSLConfig
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import SSLSocketFactory from 'javax.net.ssl.SSLSocketFactory';
|
||||
import { SSLConfig } from './SSLConfig.uts'
|
||||
import SSLContext from 'javax.net.ssl.SSLContext';
|
||||
import KeyStore from 'java.security.KeyStore';
|
||||
import KeyManagerFactory from 'javax.net.ssl.KeyManagerFactory';
|
||||
import CertificateFactory from 'java.security.cert.CertificateFactory';
|
||||
import TextUtils from 'android.text.TextUtils';
|
||||
|
||||
|
||||
class SSLFactoryManager {
|
||||
private static instance?: SSLFactoryManager = null;
|
||||
|
||||
private cacheSSLFactory: Map<SSLConfig, SSLSocketFactory> = new Map<SSLConfig, SSLSocketFactory>();
|
||||
|
||||
public static getInstance(): SSLFactoryManager {
|
||||
if (this.instance == null) {
|
||||
this.instance = SSLFactoryManager();
|
||||
}
|
||||
return this.instance!;
|
||||
}
|
||||
|
||||
public getSSLSocketFactory(sslConfig: SSLConfig): SSLSocketFactory | null {
|
||||
if (sslConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (this.cacheSSLFactory.has(sslConfig)){
|
||||
let sslFactory = this.cacheSSLFactory.get(sslConfig);
|
||||
if (sslConfig != null){
|
||||
return sslFactory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try{
|
||||
let sslContext = SSLContext.getInstance('TLS');
|
||||
let keyStore = KeyStore.getInstance('PKCS12');
|
||||
let keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
|
||||
if (!TextUtils.isEmpty(sslConfig.getKeystore()) && !TextUtils.isEmpty(sslConfig.getStorePass())){
|
||||
//todo 1. 这里需要解析keystore
|
||||
// 2. 如果是文件需要转换一下路径,然后读出来。
|
||||
// resolve : 原生层会提供bundleurl和运行模式的接口。
|
||||
|
||||
}else{
|
||||
keyManagerFactory = null;
|
||||
}
|
||||
|
||||
let certificateFactory = CertificateFactory.getInstance('X.509');
|
||||
let caKeyStore = KeyStore.getInstance('PKCS12');
|
||||
|
||||
|
||||
|
||||
}catch(e : Exception){
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
SSLFactoryManager
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import RequestBody from 'okhttp3.RequestBody';
|
||||
import MediaType from 'okhttp3.MediaType';
|
||||
import InputStream from 'java.io.InputStream';
|
||||
import BufferedSink from 'okio.BufferedSink';
|
||||
import Source from 'okio.Source';
|
||||
import Okio from 'okio.Okio';
|
||||
import Util from 'okhttp3.internal.Util';
|
||||
|
||||
|
||||
|
||||
export class InputStreamRequestBody extends RequestBody {
|
||||
private mediaType : MediaType | null = null;
|
||||
private length : Long = -1;
|
||||
private inputStream : InputStream | null = null;
|
||||
|
||||
constructor(mediaType : MediaType, length : Long, inputStream : InputStream) {
|
||||
super()
|
||||
this.mediaType = mediaType;
|
||||
this.length = length;
|
||||
this.inputStream = inputStream;
|
||||
}
|
||||
|
||||
|
||||
override contentLength() : Long {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
override contentType() : MediaType {
|
||||
const type = this.mediaType;
|
||||
if (type == null) {
|
||||
return MediaType.parse("application/octet-stream")!;
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
override writeTo(sink : BufferedSink) {
|
||||
let source : Source | null = null;
|
||||
try {
|
||||
source = Okio.source(this.inputStream);
|
||||
sink.writeAll(source);
|
||||
} catch (e) {
|
||||
}
|
||||
Util.closeQuietly(source);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import RequestBody from 'okhttp3.RequestBody';
|
||||
import MediaType from 'okhttp3.MediaType';
|
||||
import BufferedSink from 'okio.BufferedSink';
|
||||
import ForwardingSink from 'okio.ForwardingSink';
|
||||
import Sink from 'okio.Sink';
|
||||
import Buffer from 'okio.Buffer';
|
||||
import Okio from 'okio.Okio';
|
||||
|
||||
export interface UploadProgressListener {
|
||||
onProgress(bytesWritten : number, contentLength : number) : void;
|
||||
}
|
||||
|
||||
class CountingSink extends ForwardingSink {
|
||||
private listener : UploadProgressListener | null = null;
|
||||
private bytesWritten : number = 0;
|
||||
private total : number = 0;
|
||||
constructor(sink : Sink, total : number, listener : UploadProgressListener) {
|
||||
super(sink)
|
||||
this.listener = listener;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
override write(source : Buffer, byteCount : Long) {
|
||||
super.write(source, byteCount);
|
||||
this.bytesWritten += byteCount;
|
||||
this.listener?.onProgress(this.bytesWritten, this.total);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class ProgressRequestBody extends RequestBody {
|
||||
private requestBody : RequestBody | null = null;
|
||||
private listener : UploadProgressListener | null = null;
|
||||
constructor(requestBody : RequestBody, listener : UploadProgressListener) {
|
||||
super();
|
||||
this.requestBody = requestBody;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
||||
override contentLength() : Long {
|
||||
return this.requestBody?.contentLength() ?? 0;
|
||||
}
|
||||
|
||||
override contentType() : MediaType {
|
||||
const body = this.requestBody;
|
||||
if (body == null) {
|
||||
return MediaType.parse("application/octet-stream")!;
|
||||
} else {
|
||||
return body.contentType()!;
|
||||
}
|
||||
}
|
||||
|
||||
override writeTo(sink : BufferedSink) {
|
||||
const countingSink = new CountingSink(sink, this.contentLength(), this.listener!);
|
||||
const bufferedSink = Okio.buffer(countingSink);
|
||||
this.requestBody?.writeTo(bufferedSink);
|
||||
bufferedSink.flush();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
import { UploadFileOptions, UploadTask, UploadFileProgressUpdateCallback, UploadFileOptionFiles, OnProgressUpdateResult } from '../../../interface.uts';
|
||||
import { NetworkUploadFileListener } from '../NetworkManager.uts'
|
||||
import OkHttpClient from 'okhttp3.OkHttpClient';
|
||||
import TimeUnit from 'java.util.concurrent.TimeUnit';
|
||||
import ExecutorService from 'java.util.concurrent.ExecutorService';
|
||||
import Executors from 'java.util.concurrent.Executors';
|
||||
import RequestBody from 'okhttp3.RequestBody';
|
||||
import MediaType from 'okhttp3.MediaType';
|
||||
import MultipartBody from 'okhttp3.MultipartBody';
|
||||
import Call from 'okhttp3.Call';
|
||||
import Dispatcher from 'okhttp3.Dispatcher';
|
||||
import Request from 'okhttp3.Request';
|
||||
import MimeTypeMap from 'android.webkit.MimeTypeMap';
|
||||
import TextUtils from 'android.text.TextUtils';
|
||||
import File from 'java.io.File';
|
||||
import Uri from 'android.net.Uri';
|
||||
import InputStream from 'java.io.InputStream';
|
||||
import MediaStore from 'android.provider.MediaStore';
|
||||
import FileInputStream from 'java.io.FileInputStream';
|
||||
import { InputStreamRequestBody } from './InputStreamRequestBody.uts';
|
||||
import { UploadProgressListener, ProgressRequestBody } from './ProgressRequestBody.uts'
|
||||
import Callback from 'okhttp3.Callback';
|
||||
import Response from 'okhttp3.Response';
|
||||
import IOException from 'java.io.IOException';
|
||||
import Retention from 'java.lang.annotation.Retention';
|
||||
import URI from 'java.net.URI';
|
||||
import Build from 'android.os.Build';
|
||||
import Environment from 'android.os.Environment';
|
||||
import UUID from 'java.util.UUID';
|
||||
import Context from 'android.content.Context';
|
||||
import FileOutputStream from 'java.io.FileOutputStream';
|
||||
import { CookieInterceptor } from '../interceptor/CookieInterceptor.uts'
|
||||
|
||||
|
||||
class FileInformation {
|
||||
public inputStream : InputStream | null = null;
|
||||
public size : Long = -1;
|
||||
public mime : string | null = null;
|
||||
public name : string | null = null;
|
||||
}
|
||||
|
||||
class NetworkUploadTaskImpl implements UploadTask {
|
||||
private call : Call | null = null;
|
||||
private listener : NetworkUploadFileListener | null = null;
|
||||
constructor(call : Call | null, listener : NetworkUploadFileListener) {
|
||||
this.call = call;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public abort() {
|
||||
if (this.call != null) {
|
||||
this.call?.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public onProgressUpdate(option : UploadFileProgressUpdateCallback) {
|
||||
const kListener = this.listener;
|
||||
if (kListener != null) {
|
||||
kListener.progressListeners.add(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkUploadProgressListener implements UploadProgressListener {
|
||||
private listener : NetworkUploadFileListener | null = null;
|
||||
constructor(listener : NetworkUploadFileListener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
onProgress(bytesWritten : number, contentLength : number) {
|
||||
const progress = (bytesWritten.toFloat() / contentLength) * 100
|
||||
const progressUpdate : OnProgressUpdateResult = {
|
||||
progress: progress.toInt(),
|
||||
totalBytesSent: bytesWritten,
|
||||
totalBytesExpectedToSend: contentLength
|
||||
}
|
||||
|
||||
this.listener?.onProgress(progressUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
class UploadController {
|
||||
private static instance : UploadController | null = null
|
||||
|
||||
/**
|
||||
* 上传的线程池
|
||||
*/
|
||||
private uploadExecutorService : ExecutorService | null = null;
|
||||
|
||||
public static getInstance() : UploadController {
|
||||
if (this.instance == null) {
|
||||
this.instance = new UploadController();
|
||||
}
|
||||
return this.instance!;
|
||||
}
|
||||
|
||||
public uploadFile(options : UploadFileOptions, listener : NetworkUploadFileListener) : UploadTask {
|
||||
const client = this.createUploadClient(options);
|
||||
|
||||
let request = this.createUploadRequest(options, listener);
|
||||
if (request == null) {
|
||||
return new NetworkUploadTaskImpl(null, listener);;
|
||||
}
|
||||
let call : Call = client.newCall(request);
|
||||
call.enqueue(new SimpleUploadCallback(listener));
|
||||
|
||||
let task = new NetworkUploadTaskImpl(call, listener);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
private createUploadClient(option : UploadFileOptions) : OkHttpClient {
|
||||
|
||||
let clientBuilder = OkHttpClient.Builder();
|
||||
const timeout : Long = option.timeout != null ? option.timeout!.toLong() : 120000;
|
||||
clientBuilder.connectTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
clientBuilder.readTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
clientBuilder.writeTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
clientBuilder.addInterceptor(new CookieInterceptor());
|
||||
|
||||
if (this.uploadExecutorService == null) {
|
||||
this.uploadExecutorService = Executors.newFixedThreadPool(10);
|
||||
}
|
||||
|
||||
clientBuilder.dispatcher(new Dispatcher(this.uploadExecutorService));
|
||||
|
||||
return clientBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
private createUploadRequest(options : UploadFileOptions, listener : NetworkUploadFileListener) : Request | null {
|
||||
let requestBilder = new Request.Builder();
|
||||
try {
|
||||
requestBilder.url(options.url);
|
||||
} catch (exception : Exception) {
|
||||
let option = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '600009';
|
||||
option['errorMsg'] = "invalid URL";
|
||||
let cause = exception.cause.toString();
|
||||
option['cause'] = new SourceError(cause);
|
||||
if (listener != null) {
|
||||
listener.onComplete(option);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let multiPartBody = (new MultipartBody.Builder("----" + UUID.randomUUID().toString())).setType(MultipartBody.FORM);
|
||||
|
||||
const formData = options.formData?.toMap();
|
||||
if (formData != null) {
|
||||
for (entry in formData) {
|
||||
const key = entry.key;
|
||||
const value = entry.value;
|
||||
if (value != null) {
|
||||
multiPartBody.addFormDataPart(key, "" + value);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tempFiles = options.files;
|
||||
if (tempFiles != null && tempFiles!.length > 0) {
|
||||
const files : UploadFileOptionFiles[] = tempFiles;
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const path = file.uri;
|
||||
const fileInformation = this.getFileInformation(path)
|
||||
const name = file.name ?? "file";
|
||||
|
||||
const inputStream = fileInformation?.inputStream;
|
||||
if (fileInformation != null && inputStream != null) {
|
||||
let requestBody = new InputStreamRequestBody(MediaType.parse(fileInformation.mime ?? "*/*")!, fileInformation.size, inputStream);
|
||||
multiPartBody.addFormDataPart(name, fileInformation.name, requestBody);
|
||||
} else {
|
||||
let option = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '-1';
|
||||
option['errorMsg'] = "Illegal file";
|
||||
option['cause'] = null;
|
||||
if (listener != null) {
|
||||
listener.onComplete(option);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const filePath = options.filePath;
|
||||
if (filePath == null) {
|
||||
let option = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '-1';
|
||||
option['errorMsg'] = "filePath is null";
|
||||
option['cause'] = null;
|
||||
if (listener != null) {
|
||||
listener.onComplete(option);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const fileInformation = this.getFileInformation(filePath);
|
||||
const name = options.name ?? "file";
|
||||
|
||||
const inputStream = fileInformation?.inputStream;
|
||||
if (fileInformation != null && inputStream != null) {
|
||||
let requestBody = new InputStreamRequestBody(MediaType.parse(fileInformation.mime ?? "*/*")!, fileInformation.size, inputStream);
|
||||
multiPartBody.addFormDataPart(name, fileInformation.name, requestBody);
|
||||
} else {
|
||||
let option = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '-1';
|
||||
option['errorMsg'] = "Illegal file";
|
||||
option['cause'] = null;
|
||||
if (listener != null) {
|
||||
listener.onComplete(option);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let ua = UTSAndroid.getWebViewInfo(UTSAndroid.getAppContext()!)["ua"].toString();
|
||||
requestBilder.header("User-Agent", ua);
|
||||
|
||||
const headers = options.header?.toMap();
|
||||
if (headers != null) {
|
||||
for (entry in headers) {
|
||||
const key = entry.key;
|
||||
const value = entry.value;
|
||||
|
||||
if (value != null) {
|
||||
requestBilder.addHeader(key, "" + value);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestBilder.post(new ProgressRequestBody(multiPartBody.build(), new NetworkUploadProgressListener(listener)));
|
||||
|
||||
return requestBilder.build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件信息对象
|
||||
*/
|
||||
private getFileInformation(uri : string) : FileInformation | null {
|
||||
let result : FileInformation | null = null;
|
||||
if (uri.startsWith("content://")) {
|
||||
const contentUri = Uri.parse(uri);
|
||||
const context = UTSAndroid.getAppContext();
|
||||
let cursor = context!.getContentResolver().query(contentUri, null, null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToFirst();
|
||||
let fileInformation = new FileInformation();
|
||||
fileInformation.inputStream = context.getContentResolver().openInputStream(contentUri);
|
||||
fileInformation.size = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media.SIZE)).toLong();
|
||||
fileInformation.name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
|
||||
fileInformation.mime = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE))
|
||||
result = fileInformation;
|
||||
cursor.close()
|
||||
}
|
||||
} else {
|
||||
if (uri.startsWith("file://")) {
|
||||
uri = uri.substring("file://".length)
|
||||
} else if(uri.startsWith("unifile://")){
|
||||
uri = UTSAndroid.convert2AbsFullPath(uri)
|
||||
}else {
|
||||
// 如果不是file://开头的,就说明是相对路径。
|
||||
uri = UTSAndroid.convert2AbsFullPath(uri)
|
||||
if(uri.startsWith("/android_asset/")){
|
||||
const filePath = uri.replace("/android_asset/", "")
|
||||
const context = UTSAndroid.getAppContext();
|
||||
const apkFile = this.copyAssetFileToPrivateDir(context!!, filePath)
|
||||
if(apkFile != null){
|
||||
uri = apkFile.getPath()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let file = new File(uri);
|
||||
let fileInputStream = new FileInputStream(file);
|
||||
let size = file.length();
|
||||
let name = file.getName();
|
||||
let mime = this.getMimeType(name);
|
||||
|
||||
let fileInformation = new FileInformation();
|
||||
fileInformation.inputStream = fileInputStream;
|
||||
fileInformation.size = size;
|
||||
fileInformation.name = name;
|
||||
fileInformation.mime = mime;
|
||||
result = fileInformation;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private copyAssetFileToPrivateDir(context: Context, fileName: string): File| null {
|
||||
try {
|
||||
const destPath = context.getCacheDir().getPath() + "/uploadFiles/" + fileName
|
||||
const outFile = new File(destPath)
|
||||
const parentFile = outFile.getParentFile()
|
||||
if (parentFile != null) {
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs()
|
||||
}
|
||||
}
|
||||
if(!outFile.exists()){
|
||||
outFile.createNewFile()
|
||||
}
|
||||
const inputStream = context.getAssets().open(fileName)
|
||||
const outputStream = new FileOutputStream(outFile)
|
||||
let buffer = new ByteArray(1024);
|
||||
do {
|
||||
let len = inputStream.read(buffer);
|
||||
if (len == -1) {
|
||||
break;
|
||||
}
|
||||
outputStream.write(buffer, 0, len)
|
||||
} while (true)
|
||||
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
|
||||
return outFile
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
private checkPrivatePath(path : string) : boolean {
|
||||
if (Build.VERSION.SDK_INT > 29 && Environment.isExternalStorageManager()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.startsWith("file://")) {
|
||||
path = path.replace("file://", "");
|
||||
}
|
||||
const context = UTSAndroid.getAppContext()!;
|
||||
let cache = context.getExternalCacheDir();
|
||||
let sPrivateExternalDir = ""
|
||||
if (cache == null) {
|
||||
sPrivateExternalDir = Environment.getExternalStorageDirectory().getPath() + "/Android/data/" + context.getPackageName();
|
||||
} else {
|
||||
sPrivateExternalDir = cache.getParent();
|
||||
}
|
||||
const sPrivateDir = context.getFilesDir().getParent();
|
||||
|
||||
if (sPrivateExternalDir.startsWith("/") && !path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
|
||||
if ((path.contains(sPrivateDir) || path.contains(sPrivateExternalDir))//表示应用私有路径
|
||||
|| this.isAssetFile(path) //表示apk的assets路径文件
|
||||
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.Q//表示当前手机属于可正常访问路径系统
|
||||
) {
|
||||
//文件路径在私有路径下或手机系统版符合非分区存储逻辑
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private isAssetFile(filePath : string) : boolean {
|
||||
let isAsset = false;
|
||||
if (filePath.startsWith("apps/")) {
|
||||
isAsset = true;
|
||||
} else if (filePath.startsWith("/android_asset/") || filePath.startsWith("android_asset/")) {
|
||||
isAsset = true;
|
||||
}
|
||||
return isAsset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件mime
|
||||
*/
|
||||
private getMimeType(filename : string) : string {
|
||||
let map = MimeTypeMap.getSingleton()
|
||||
var extType = MimeTypeMap.getFileExtensionFromUrl(filename)
|
||||
if (extType == null && filename.lastIndexOf(".") >= 0) {
|
||||
extType = filename.substring(filename.lastIndexOf(".") + 1)
|
||||
}
|
||||
let ret = map.getMimeTypeFromExtension(extType);
|
||||
if (TextUtils.isEmpty(ret)) {
|
||||
if (TextUtils.isEmpty(extType)) {
|
||||
ret = "*/*"
|
||||
} else {
|
||||
ret = "application/" + extType
|
||||
}
|
||||
}
|
||||
|
||||
return ret!;
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleUploadCallback implements Callback {
|
||||
private listener : NetworkUploadFileListener | null = null;
|
||||
constructor(listener : NetworkUploadFileListener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
||||
override onFailure(call : Call, exception : IOException) {
|
||||
let option = {};
|
||||
option['statusCode'] = '-1';
|
||||
option['errorCode'] = '-1';
|
||||
option['errorMsg'] = exception.message;
|
||||
let cause = exception.cause.toString();
|
||||
option['cause'] = new SourceError(cause);
|
||||
this.listener?.onComplete(option);
|
||||
}
|
||||
override onResponse(call : Call, response : Response) {
|
||||
const result = {};
|
||||
result["statusCode"] = response.code() + "";
|
||||
result["data"] = response.body()?.string();
|
||||
this.listener?.onComplete(result);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
UploadController
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
|
||||
export class NetworkUtil {
|
||||
public static convertHeaders(headers: MutableMap<String, MutableList<String>>): UTSJSONObject {
|
||||
let simpleHeaders = {};
|
||||
if (headers != null) {
|
||||
let it = headers.iterator();
|
||||
while (it.hasNext()) {
|
||||
let entry = it.next();
|
||||
let key = entry.key;
|
||||
let value = entry.value;
|
||||
|
||||
let tmpKey = '_';
|
||||
if (key == null) {
|
||||
key = tmpKey;
|
||||
}
|
||||
|
||||
if (value.size == 0) {
|
||||
continue;
|
||||
} else if (value.size == 1) {
|
||||
simpleHeaders[key] = value.get(0);
|
||||
} else {
|
||||
simpleHeaders[key] = value.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return simpleHeaders;
|
||||
}
|
||||
|
||||
public static parseCookie(header: UTSJSONObject | null): string[] {
|
||||
if (header == null) {
|
||||
return []
|
||||
}
|
||||
let cookiesStr = header.getString('Set-Cookie')
|
||||
if (cookiesStr == null) {
|
||||
cookiesStr = header.getString('set-cookie')
|
||||
}
|
||||
if (cookiesStr == null) {
|
||||
return []
|
||||
}
|
||||
let cookiesArr = new Array<string>()
|
||||
if (cookiesStr.charAt(0) == "[" && cookiesStr.charAt(cookiesStr.length - 1) == "]") {
|
||||
cookiesStr = cookiesStr.slice(1, -1)
|
||||
}
|
||||
|
||||
const handleCookiesArr = cookiesStr.split(';')
|
||||
for (let i = 0; i < handleCookiesArr.length; i++) {
|
||||
if (handleCookiesArr[i].indexOf('Expires=') != -1 || handleCookiesArr[i].indexOf('expires=') != -1) {
|
||||
cookiesArr.push(handleCookiesArr[i].replace(',', ''))
|
||||
} else {
|
||||
cookiesArr.push(handleCookiesArr[i])
|
||||
}
|
||||
}
|
||||
cookiesArr = cookiesArr.join(';').split(',')
|
||||
|
||||
return cookiesArr
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user