Geen omschrijving

index.js 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /**
  2. * @author wkh237
  3. * @version 0.5.0
  4. * @flow
  5. */
  6. import {
  7. NativeModules,
  8. DeviceEventEmitter,
  9. NativeAppEventEmitter,
  10. Platform,
  11. } from 'react-native'
  12. type fetchConfig = {
  13. fileCache : bool,
  14. path : string,
  15. appendExt : string
  16. };
  17. type RNFetchBlobNative = {
  18. fetchBlob : (
  19. options:fetchConfig,
  20. taskId:string,
  21. method:string,
  22. url:string,
  23. headers:any,
  24. body:any,
  25. callback:(err:any, ...data:any) => void
  26. ) => void,
  27. fetchBlobForm : (
  28. options:fetchConfig,
  29. taskId:string,
  30. method:string,
  31. url:string,
  32. headers:any,
  33. form:Array<any>,
  34. callback:(err:any, ...data:any) => void
  35. ) => void,
  36. readStream : (
  37. path:string,
  38. encode: 'utf8' | 'ascii' | 'base64'
  39. ) => void,
  40. getEnvironmentDirs : (dirs:any) => void,
  41. flush : () => void
  42. };
  43. import base64 from 'base-64'
  44. const emitter = (Platform.OS === 'android' ? DeviceEventEmitter : NativeAppEventEmitter)
  45. const RNFetchBlob:RNFetchBlobNative = NativeModules.RNFetchBlob
  46. emitter.addListener("RNFetchBlobMessage", (e) => {
  47. if(e.event === 'warn') {
  48. console.warn(e.detail)
  49. }
  50. else if (e.event === 'error') {
  51. throw e.detail
  52. }
  53. else {
  54. console.log("RNFetchBlob native message", e.detail)
  55. }
  56. })
  57. // Show warning if native module not detected
  58. if(!RNFetchBlob || !RNFetchBlob.fetchBlobForm || !RNFetchBlob.fetchBlob) {
  59. console.warn(
  60. 'react-native-fetch-blob could not find valid native module.',
  61. 'please make sure you have linked native modules using `rnpm link`,',
  62. 'and restart RN packager or manually compile IOS/Android project.'
  63. )
  64. }
  65. function getSystemDirs() {
  66. return new Promise((resolve, reject) => {
  67. try {
  68. RNFetchBlob.getEnvironmentDirs((...dirs) => {
  69. let [PictureDir, MovieDir, DocumentDir, CacheDir] = [...dirs]
  70. resolve({PictureDir, MovieDir, DocumentDir, CacheDir})
  71. })
  72. } catch(err) {
  73. reject(err)
  74. }
  75. })
  76. }
  77. function config (options:fetchConfig) {
  78. return { fetch : fetch.bind(options) }
  79. }
  80. // Promise wrapper function
  81. function fetch(...args:any) {
  82. let options = this || {}
  83. // create task ID for receiving progress event
  84. let taskId = getUUID()
  85. let promise = new Promise((resolve, reject) => {
  86. let [method, url, headers, body] = [...args]
  87. let nativeMethodName = Array.isArray(body) ? 'fetchBlobForm' : 'fetchBlob'
  88. // on progress event listener
  89. let subscription = emitter.addListener('RNFetchBlobProgress', (e) => {
  90. if(e.taskId === taskId && promise.onProgress) {
  91. promise.onProgress(e.written, e.total)
  92. }
  93. })
  94. let req = RNFetchBlob[nativeMethodName]
  95. req(options, taskId, method, url, headers || {}, body, (err, ...data) => {
  96. // task done, remove event listener
  97. subscription.remove()
  98. if(err)
  99. reject(new Error(err, ...data))
  100. else {
  101. let respType = 'base64'
  102. if(options.path || options.fileCache)
  103. respType = 'path'
  104. resolve(new FetchBlobResponse(taskId, respType, ...data))
  105. }
  106. })
  107. })
  108. promise.progress = (fn) => {
  109. promise.onProgress = fn
  110. return promise
  111. }
  112. return promise
  113. }
  114. /**
  115. * RNFetchBlob response object class.
  116. */
  117. class FetchBlobResponse {
  118. taskId : string;
  119. path : () => string | null;
  120. type : 'base64' | 'path';
  121. data : any;
  122. blob : (contentType:string, sliceSize:number) => null;
  123. text : () => string;
  124. json : () => any;
  125. base64 : () => any;
  126. flush : () => void;
  127. readStream : (
  128. encode: 'utf8' | 'ascii' | 'base64',
  129. fn:(event : 'data' | 'end', chunk:any) => void
  130. ) => void;
  131. constructor(taskId:string, type:'base64' | 'path', data:any) {
  132. this.data = data
  133. this.taskId = taskId
  134. this.type = type
  135. /**
  136. * Convert result to javascript Blob object.
  137. * @param {string} contentType MIME type of the blob object.
  138. * @param {number} sliceSize Slice size.
  139. * @return {blob} Return Blob object.
  140. */
  141. this.blob = (contentType:string, sliceSize:number) => {
  142. console.warn('FetchBlobResponse.blob() is deprecated and has no funtionality.')
  143. return null
  144. }
  145. /**
  146. * Convert result to text.
  147. * @return {string} Decoded base64 string.
  148. */
  149. this.text = ():string => {
  150. return base64.decode(this.data)
  151. }
  152. /**
  153. * Convert result to JSON object.
  154. * @return {object} Parsed javascript object.
  155. */
  156. this.json = ():any => {
  157. return JSON.parse(base64.decode(this.data))
  158. }
  159. /**
  160. * Return BASE64 string directly.
  161. * @return {string} BASE64 string of response body.
  162. */
  163. this.base64 = ():string => {
  164. return this.data
  165. }
  166. /**
  167. * Remove cahced file
  168. * @return {void}
  169. */
  170. this.flush = () => {
  171. RNFetchBlob.flush(this.path())
  172. }
  173. this.path = () => {
  174. if(this.type === 'path')
  175. return this.data
  176. return null
  177. }
  178. /**
  179. * Start read stream from cached file
  180. * @param {String} encoding Encode type, should be one of `base64`, `ascrii`, `utf8`.
  181. * @param {Function} fn On data event handler
  182. * @return {void}
  183. */
  184. this.readStream = (encode: 'base64' | 'utf8' | 'ascii', fn) => {
  185. // register for file stream event
  186. let subscription = emitter.addListener(`RNFetchBlobStream${this.taskId}`, (event, chunk) => {
  187. fn(event, chunk)
  188. // when stream closed, remove event handler
  189. if(event === 'end')
  190. subscription()
  191. })
  192. if(this.type === 'path') {
  193. RNFetchBlob.readStream(this.data, encode)
  194. }
  195. else {
  196. console.warn('RNFetchblob', 'this response data does not contains any available stream')
  197. }
  198. }
  199. }
  200. }
  201. function getUUID(){
  202. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  203. let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
  204. return v.toString(16);
  205. });
  206. }
  207. export default {
  208. fetch, base64, config, getSystemDirs
  209. }