Nav apraksta

RNFetchBlobReq.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. package com.RNFetchBlob;
  2. import android.app.DownloadManager;
  3. import android.content.BroadcastReceiver;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.IntentFilter;
  7. import android.database.Cursor;
  8. import android.net.Uri;
  9. import android.util.Base64;
  10. import android.util.Log;
  11. import com.RNFetchBlob.Response.RNFetchBlobDefaultResp;
  12. import com.RNFetchBlob.Response.RNFetchBlobFileResp;
  13. import com.facebook.react.bridge.Arguments;
  14. import com.facebook.react.bridge.Callback;
  15. import com.facebook.react.bridge.ReactApplicationContext;
  16. import com.facebook.react.bridge.ReadableArray;
  17. import com.facebook.react.bridge.ReadableMap;
  18. import com.facebook.react.bridge.ReadableMapKeySetIterator;
  19. import com.facebook.react.bridge.WritableMap;
  20. import com.facebook.react.modules.core.DeviceEventManagerModule;
  21. import java.io.ByteArrayInputStream;
  22. import java.io.File;
  23. import java.io.FileInputStream;
  24. import java.io.FileOutputStream;
  25. import java.io.IOException;
  26. import java.io.InputStream;
  27. import java.net.MalformedURLException;
  28. import java.net.URL;
  29. import java.util.HashMap;
  30. import okhttp3.Call;
  31. import okhttp3.Headers;
  32. import okhttp3.Interceptor;
  33. import okhttp3.MediaType;
  34. import okhttp3.OkHttpClient;
  35. import okhttp3.Request;
  36. import okhttp3.RequestBody;
  37. import okhttp3.Response;
  38. import okhttp3.ResponseBody;
  39. import okhttp3.FormBody;
  40. import okhttp3.internal.framed.Header;
  41. import okhttp3.internal.http.OkHeaders;
  42. /**
  43. * Created by wkh237 on 2016/6/21.
  44. */
  45. public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
  46. enum RequestType {
  47. Form,
  48. SingleFile,
  49. AsIs,
  50. WithoutBody,
  51. Others
  52. };
  53. enum ResponseType {
  54. KeepInMemory,
  55. FileStorage
  56. };
  57. static HashMap<String, Call> taskTable = new HashMap<>();
  58. MediaType contentType = RNFetchBlobConst.MIME_OCTET;
  59. ReactApplicationContext ctx;
  60. RNFetchBlobConfig options;
  61. String taskId;
  62. String method;
  63. String url;
  64. String rawRequestBody;
  65. String destPath;
  66. ReadableArray rawRequestBodyArray;
  67. ReadableMap headers;
  68. Callback callback;
  69. long contentLength;
  70. long downloadManagerId;
  71. RequestType requestType;
  72. ResponseType responseType;
  73. public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, final Callback callback) {
  74. this.method = method;
  75. this.options = new RNFetchBlobConfig(options);
  76. this.taskId = taskId;
  77. this.url = url;
  78. this.headers = headers;
  79. this.callback = callback;
  80. this.rawRequestBody = body;
  81. this.rawRequestBodyArray = arrayBody;
  82. if(this.options.fileCache || this.options.path != null)
  83. responseType = ResponseType.FileStorage;
  84. else
  85. responseType = ResponseType.KeepInMemory;
  86. if (body != null)
  87. requestType = RequestType.SingleFile;
  88. else if (arrayBody != null)
  89. requestType = RequestType.Form;
  90. else
  91. requestType = RequestType.WithoutBody;
  92. }
  93. public static void cancelTask(String taskId) {
  94. if(taskTable.containsKey(taskId)) {
  95. Call call = taskTable.get(taskId);
  96. call.cancel();
  97. taskTable.remove(taskId);
  98. }
  99. }
  100. @Override
  101. public void run() {
  102. // use download manager instead of default HTTP implementation
  103. if (options.addAndroidDownloads != null && options.addAndroidDownloads.hasKey("useDownloadManager")) {
  104. if (options.addAndroidDownloads.getBoolean("useDownloadManager")) {
  105. Uri uri = Uri.parse(url);
  106. DownloadManager.Request req = new DownloadManager.Request(uri);
  107. req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
  108. if (options.addAndroidDownloads.hasKey("title")) {
  109. req.setTitle(options.addAndroidDownloads.getString("title"));
  110. }
  111. if (options.addAndroidDownloads.hasKey("description")) {
  112. req.setDescription(options.addAndroidDownloads.getString("description"));
  113. }
  114. // set headers
  115. ReadableMapKeySetIterator it = headers.keySetIterator();
  116. while (it.hasNextKey()) {
  117. String key = it.nextKey();
  118. req.addRequestHeader(key, headers.getString(key));
  119. }
  120. Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
  121. DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
  122. downloadManagerId = dm.enqueue(req);
  123. appCtx.registerReceiver(this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
  124. return;
  125. }
  126. }
  127. // find cached result if `key` property exists
  128. String cacheKey = this.taskId;
  129. String ext = this.options.appendExt != "" ? "." + this.options.appendExt : "";
  130. if (this.options.key != null) {
  131. cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
  132. if (cacheKey == null) {
  133. cacheKey = this.taskId;
  134. }
  135. File file = new File(RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext);
  136. if (file.exists()) {
  137. callback.invoke(null, file.getAbsolutePath());
  138. return;
  139. }
  140. }
  141. if(this.options.path != null)
  142. this.destPath = this.options.path;
  143. else if(this.options.fileCache == true)
  144. this.destPath = RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext;
  145. OkHttpClient.Builder clientBuilder;
  146. try {
  147. // use trusty SSL socket
  148. if (this.options.trusty) {
  149. clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient();
  150. } else {
  151. clientBuilder = new OkHttpClient.Builder();
  152. }
  153. final Request.Builder builder = new Request.Builder();
  154. try {
  155. builder.url(new URL(url));
  156. } catch (MalformedURLException e) {
  157. e.printStackTrace();
  158. }
  159. HashMap<String, String> mheaders = new HashMap<>();
  160. // set headers
  161. if (headers != null) {
  162. ReadableMapKeySetIterator it = headers.keySetIterator();
  163. while (it.hasNextKey()) {
  164. String key = it.nextKey();
  165. String value = headers.getString(key);
  166. builder.header(key, value);
  167. mheaders.put(key,value);
  168. }
  169. }
  170. if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put")) {
  171. String cType = getHeaderIgnoreCases(mheaders, "content-type").toLowerCase();
  172. if(cType == null) {
  173. builder.header("Content-Type", "application/octet-stream");
  174. requestType = RequestType.SingleFile;
  175. }
  176. if(rawRequestBody != null) {
  177. if(rawRequestBody.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
  178. requestType = RequestType.SingleFile;
  179. }
  180. else if (cType.contains(";base64") || cType.startsWith("application/octet")) {
  181. requestType = RequestType.SingleFile;
  182. } else {
  183. requestType = RequestType.AsIs;
  184. }
  185. }
  186. }
  187. else {
  188. requestType = RequestType.WithoutBody;
  189. }
  190. // set request body
  191. switch (requestType) {
  192. case SingleFile:
  193. builder.method(method, new RNFetchBlobBody(
  194. taskId,
  195. requestType,
  196. rawRequestBody,
  197. MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type"))
  198. ));
  199. break;
  200. case AsIs:
  201. builder.method(method, new RNFetchBlobBody(
  202. taskId,
  203. requestType,
  204. rawRequestBody,
  205. MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type"))
  206. ));
  207. break;
  208. case Form:
  209. builder.method(method, new RNFetchBlobBody(
  210. taskId,
  211. requestType,
  212. rawRequestBodyArray,
  213. MediaType.parse("multipart/form-data; boundary=RNFetchBlob-" + taskId)
  214. ));
  215. break;
  216. case WithoutBody:
  217. builder.method(method, null);
  218. break;
  219. }
  220. final Request req = builder.build();
  221. // create response handler
  222. clientBuilder.addInterceptor(new Interceptor() {
  223. @Override
  224. public Response intercept(Chain chain) throws IOException {
  225. Response originalResponse = chain.proceed(req);
  226. ResponseBody extended;
  227. switch (responseType) {
  228. case KeepInMemory:
  229. extended = new RNFetchBlobDefaultResp(
  230. RNFetchBlob.RCTContext,
  231. taskId,
  232. originalResponse.body());
  233. break;
  234. case FileStorage:
  235. extended = new RNFetchBlobFileResp(
  236. RNFetchBlob.RCTContext,
  237. taskId,
  238. originalResponse.body(),
  239. destPath);
  240. break;
  241. default:
  242. extended = new RNFetchBlobDefaultResp(
  243. RNFetchBlob.RCTContext,
  244. taskId,
  245. originalResponse.body());
  246. break;
  247. }
  248. return originalResponse.newBuilder().body(extended).build();
  249. }
  250. });
  251. OkHttpClient client = clientBuilder.build();
  252. Call call = client.newCall(req);
  253. taskTable.put(taskId, call);
  254. call.enqueue(new okhttp3.Callback() {
  255. @Override
  256. public void onFailure(Call call, IOException e) {
  257. callback.invoke(e.getLocalizedMessage(), null);
  258. }
  259. @Override
  260. public void onResponse(Call call, Response response) throws IOException {
  261. ReadableMap notifyConfig = options.addAndroidDownloads;
  262. // Download manager settings
  263. if(notifyConfig != null ) {
  264. String title = "", desc = "", mime = "text/plain";
  265. boolean scannable = false, notification = false;
  266. if(notifyConfig.hasKey("title"))
  267. title = options.addAndroidDownloads.getString("title");
  268. if(notifyConfig.hasKey("description"))
  269. desc = notifyConfig.getString("description");
  270. if(notifyConfig.hasKey("mime"))
  271. mime = notifyConfig.getString("mime");
  272. if(notifyConfig.hasKey("mediaScannable"))
  273. scannable = notifyConfig.getBoolean("mediaScannable");
  274. if(notifyConfig.hasKey("notification"))
  275. notification = notifyConfig.getBoolean("notification");
  276. DownloadManager dm = (DownloadManager)RNFetchBlob.RCTContext.getSystemService(RNFetchBlob.RCTContext.DOWNLOAD_SERVICE);
  277. dm.addCompletedDownload(title, desc, scannable, mime, destPath, contentLength, notification);
  278. }
  279. done(response);
  280. }
  281. });
  282. } catch (Exception error) {
  283. error.printStackTrace();
  284. taskTable.remove(taskId);
  285. callback.invoke("RNFetchBlob request error: " + error.getMessage() + error.getCause());
  286. }
  287. }
  288. /**
  289. * Send response data back to javascript context.
  290. * @param resp OkHttp response object
  291. */
  292. private void done(Response resp) {
  293. emitStateEvent(getResponseInfo(resp));
  294. switch (responseType) {
  295. case KeepInMemory:
  296. try {
  297. // For XMLHttpRequest, automatic response data storing strategy, when response
  298. // header is not `application/json` or `text/plain`, write response data to
  299. // file system.
  300. if(isBlobResponse(resp) && options.auto == true) {
  301. String dest = RNFetchBlobFS.getTmpPath(ctx, taskId);
  302. InputStream ins = resp.body().byteStream();
  303. FileOutputStream os = new FileOutputStream(new File(dest));
  304. byte [] buffer = new byte[10240];
  305. int read = ins.read(buffer);
  306. os.write(buffer,0,read);
  307. while(read > 0) {
  308. os.write(buffer,0,read);
  309. read = ins.read(buffer);
  310. }
  311. ins.close();
  312. os.close();
  313. WritableMap info = getResponseInfo(resp);
  314. callback.invoke(null, info, dest);
  315. }
  316. else {
  317. byte[] b = resp.body().bytes();
  318. callback.invoke(null, getResponseInfo(resp), android.util.Base64.encodeToString(b, Base64.NO_WRAP));
  319. }
  320. } catch (IOException e) {
  321. callback.invoke("RNFetchBlob failed to encode response data to BASE64 string.", null);
  322. }
  323. break;
  324. case FileStorage:
  325. try{
  326. resp.body().bytes();
  327. } catch (Exception ignored) {
  328. }
  329. callback.invoke(null, getResponseInfo(resp), this.destPath);
  330. break;
  331. default:
  332. try {
  333. callback.invoke(null, getResponseInfo(resp), new String(resp.body().bytes(), "UTF-8"));
  334. } catch (IOException e) {
  335. callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
  336. }
  337. break;
  338. }
  339. if(taskTable.containsKey(taskId))
  340. taskTable.remove(taskId);
  341. }
  342. private WritableMap getResponseInfo(Response resp) {
  343. WritableMap info = Arguments.createMap();
  344. info.putInt("status", resp.code());
  345. info.putString("state", "2");
  346. info.putString("taskId", this.taskId);
  347. WritableMap headers = Arguments.createMap();
  348. for(int i =0;i< resp.headers().size();i++) {
  349. headers.putString(resp.headers().name(i), resp.headers().value(i));
  350. }
  351. info.putMap("headers", headers);
  352. Headers h = resp.headers();
  353. if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/plain"))
  354. {
  355. info.putString("respType", "text");
  356. }
  357. else if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("application/json"))
  358. {
  359. info.putString("respType", "json");
  360. }
  361. else if(getHeaderIgnoreCases(h, "content-type").length() < 1)
  362. {
  363. info.putString("respType", "blob");
  364. }
  365. else
  366. {
  367. info.putString("respType", "text");
  368. }
  369. return info;
  370. }
  371. private boolean isBlobResponse(Response resp) {
  372. Headers h = resp.headers();
  373. boolean isText = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/plain");
  374. boolean isJSON = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("application/json");
  375. return !(isJSON || isText);
  376. }
  377. private String getHeaderIgnoreCases(Headers headers, String field) {
  378. String val = headers.get(field);
  379. if(val != null) return val;
  380. return headers.get(field.toLowerCase()) == null ? "" : headers.get(field.toLowerCase());
  381. }
  382. private String getHeaderIgnoreCases(HashMap<String,String> headers, String field) {
  383. String val = headers.get(field);
  384. if(val != null) return val;
  385. return headers.get(field.toLowerCase()) == null ? "" : headers.get(field.toLowerCase());
  386. }
  387. private void emitStateEvent(WritableMap args) {
  388. RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
  389. .emit(RNFetchBlobConst.EVENT_HTTP_STATE, args);
  390. }
  391. @Override
  392. public void onReceive(Context context, Intent intent) {
  393. String action = intent.getAction();
  394. if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
  395. Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
  396. long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
  397. if (id == this.downloadManagerId) {
  398. DownloadManager.Query query = new DownloadManager.Query();
  399. query.setFilterById(downloadManagerId);
  400. DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
  401. dm.query(query);
  402. Cursor c = dm.query(query);
  403. if (c.moveToFirst()) {
  404. String contentUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
  405. Uri uri = Uri.parse(contentUri);
  406. Cursor cursor = appCtx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null);
  407. if (cursor != null) {
  408. cursor.moveToFirst();
  409. String filePath = cursor.getString(0);
  410. cursor.close();
  411. this.callback.invoke(null, null, filePath);
  412. }
  413. else
  414. this.callback.invoke(null, null, null);
  415. }
  416. }
  417. }
  418. }
  419. }