Ei kuvausta

fs.js 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /**
  2. * @name react-native-fetch-blob-fs
  3. * @author wkh237
  4. * @version 0.1.0
  5. * @flow
  6. */
  7. import {
  8. NativeModules,
  9. DeviceEventEmitter,
  10. NativeAppEventEmitter,
  11. } from 'react-native'
  12. import type {
  13. RNFetchBlobNative,
  14. RNFetchBlobConfig,
  15. RNFetchBlobStream
  16. } from './types'
  17. const RNFetchBlob:RNFetchBlobNative = NativeModules.RNFetchBlob
  18. const emitter = DeviceEventEmitter
  19. // session table
  20. let sessions = {}
  21. /**
  22. * Get path of system directories.
  23. * @return {object} Map contains DocumentDir, CacheDir, DCIMDir, DownloadDir,
  24. * , some directory might not be supported by platform.
  25. */
  26. function getSystemDirs() {
  27. return new Promise((resolve, reject) => {
  28. try {
  29. RNFetchBlob.getEnvironmentDirs((...dirs) => {
  30. let [DocumentDir, CacheDir, DCIMDir, DownloadDir] = [...dirs]
  31. resolve({DocumentDir, CacheDir, DCIMDir, DownloadDir})
  32. })
  33. } catch(err) {
  34. reject(err)
  35. }
  36. })
  37. }
  38. /**
  39. * Get a file cache session
  40. * @param {string} name Stream ID
  41. * @return {RNFetchBlobSession}
  42. */
  43. function session(name:string):RNFetchBlobSession {
  44. let s = sessions[name]
  45. if(s)
  46. return new RNFetchBlobSession(name)
  47. else {
  48. sessions[name] = []
  49. return new RNFetchBlobSession(name, [])
  50. }
  51. }
  52. function createFile(path:string, data:string, encoding: 'base64' | 'ascii' | 'utf8'):Promise {
  53. encoding = encoding || 'utf8'
  54. return new Promise((resolve, reject) => {
  55. let handler = (err) => {
  56. if(err)
  57. reject(err)
  58. else
  59. resolve()
  60. }
  61. if(encoding.toLowerCase() === 'ascii') {
  62. if(Array.isArray(data))
  63. RNFetchBlob.createFileASCII(path, data, handler)
  64. else
  65. reject('`data` of ASCII file must be an array contains numbers')
  66. }
  67. else {
  68. RNFetchBlob.createFile(path, data, encoding, handler)
  69. }
  70. })
  71. }
  72. /**
  73. * Create write stream to a file.
  74. * @param {string} path Target path of file stream.
  75. * @param {string} encoding Encoding of input data.
  76. * @param {bool} append A flag represent if data append to existing ones.
  77. * @return {Promise<WriteStream>} A promise resolves a `WriteStream` object.
  78. */
  79. function writeStream(
  80. path : string,
  81. encoding : 'utf8' | 'ascii' | 'base64',
  82. append? : ?bool,
  83. ):Promise<WriteStream> {
  84. if(!path)
  85. throw Error('RNFetchBlob could not open file stream with empty `path`')
  86. return new Promise((resolve, reject) => {
  87. RNFetchBlob.writeStream(path, encoding || 'base64', append || false, (err, streamId:string) => {
  88. if(err)
  89. reject(err)
  90. else
  91. resolve(new WriteStream(streamId, encoding))
  92. })
  93. })
  94. }
  95. /**
  96. * Create file stream from file at `path`.
  97. * @param {string} path The file path.
  98. * @param {string} encoding Data encoding, should be one of `base64`, `utf8`, `ascii`
  99. * @param {boolean} bufferSize Size of stream buffer.
  100. * @return {RNFetchBlobStream} RNFetchBlobStream stream instance.
  101. */
  102. function readStream(
  103. path : string,
  104. encoding : 'utf8' | 'ascii' | 'base64',
  105. bufferSize? : ?number
  106. ):RNFetchBlobStream {
  107. if(!path)
  108. throw Error('RNFetchBlob could not open file stream with empty `path`')
  109. encoding = encoding || 'utf8'
  110. let stream:RNFetchBlobStream = {
  111. // parse JSON array when encoding is ASCII
  112. onData : function(fn) {
  113. if(encoding.toLowerCase() === 'ascii')
  114. this._onData = (data) => {
  115. fn(JSON.parse(data))
  116. }
  117. else
  118. this._onData = fn
  119. },
  120. onError : function(fn) {
  121. this._onError = fn
  122. },
  123. onEnd : function(fn) {
  124. this._onEnd = fn
  125. },
  126. }
  127. // register for file stream event
  128. let subscription = emitter.addListener(`RNFetchBlobStream+${path}`, (e) => {
  129. let {event, detail} = e
  130. if(stream._onData && event === 'data')
  131. stream._onData(detail)
  132. else if (stream._onEnd && event === 'end') {
  133. stream._onEnd(detail)
  134. }
  135. else {
  136. if(stream._onError)
  137. stream._onError(detail)
  138. else
  139. throw new Error(detail)
  140. }
  141. // when stream closed or error, remove event handler
  142. if (event === 'error' || event === 'end') {
  143. subscription.remove()
  144. }
  145. })
  146. RNFetchBlob.readStream(path, encoding, bufferSize || 0)
  147. return stream
  148. }
  149. function mkdir(path:string):Promise {
  150. return new Promise((resolve, reject) => {
  151. RNFetchBlob.mkdir(path, (err, res) => {
  152. if(err)
  153. reject(err)
  154. else
  155. resolve()
  156. })
  157. })
  158. }
  159. function cp(path:string, dest:string):Promise<boolean> {
  160. return new Promise((resolve, reject) => {
  161. RNFetchBlob.cp(path, dest, (err, res) => {
  162. if(err)
  163. reject(err)
  164. else
  165. resolve(res)
  166. })
  167. })
  168. }
  169. function mv(path:string, dest:string):Promise<boolean> {
  170. return new Promise((resolve, reject) => {
  171. RNFetchBlob.mv(path, dest, (err, res) => {
  172. if(err)
  173. reject(err)
  174. else
  175. resolve(res)
  176. })
  177. })
  178. }
  179. function ls(path:string):Promise<Array<String>> {
  180. return new Promise((resolve, reject) => {
  181. RNFetchBlob.ls(path, (err, res) => {
  182. if(err)
  183. reject(err)
  184. else
  185. resolve(res)
  186. })
  187. })
  188. }
  189. /**
  190. * Remove file at path.
  191. * @param {string} path:string Path of target file.
  192. * @return {Promise}
  193. */
  194. function unlink(path:string):Promise {
  195. return new Promise((resolve, reject) => {
  196. RNFetchBlob.unlink(path, (err) => {
  197. if(err)
  198. reject(err)
  199. else
  200. resolve()
  201. })
  202. })
  203. }
  204. /**
  205. * Check if file exists and if it is a folder.
  206. * @param {string} path Path to check
  207. * @return {Promise<bool, bool>}
  208. */
  209. function exists(path:string):Promise<bool, bool> {
  210. return new Promise((resolve, reject) => {
  211. try {
  212. RNFetchBlob.exists(path, (exist) => {
  213. resolve(exist)
  214. })
  215. } catch(err) {
  216. reject(err)
  217. }
  218. })
  219. }
  220. function isDir(path:string):Promise<bool, bool> {
  221. return new Promise((resolve, reject) => {
  222. try {
  223. RNFetchBlob.exists(path, (exist, isDir) => {
  224. resolve(isDir)
  225. })
  226. } catch(err) {
  227. reject(err)
  228. }
  229. })
  230. }
  231. /**
  232. * Session class
  233. * @class RNFetchBlobSession
  234. */
  235. class RNFetchBlobSession {
  236. add : (path:string) => RNFetchBlobSession;
  237. remove : (path:string) => RNFetchBlobSession;
  238. dispose : () => Promise;
  239. list : () => Array<string>;
  240. name : string;
  241. constructor(name:string, list:Array<string>) {
  242. this.name = name
  243. if(!sessions[name]) {
  244. if(Array.isArray(list))
  245. sessions[name] = list
  246. else
  247. sessions[name] = []
  248. }
  249. }
  250. add(path:string):RNFetchBlobSession {
  251. sessions[this.name].push(path)
  252. return this
  253. }
  254. remove(path:string):RNFetchBlobSession {
  255. let list = sessions[this.name]
  256. for(let i in list) {
  257. if(list[i] === path) {
  258. sessions[this.name].splice(i, 1)
  259. break;
  260. }
  261. }
  262. return this
  263. }
  264. list():Array<string> {
  265. return sessions[this.name]
  266. }
  267. dispose():Promise {
  268. return new Promise((resolve, reject) => {
  269. RNFetchBlob.removeSession(sessions[this.name], (err) => {
  270. if(err)
  271. reject(err)
  272. else {
  273. delete sessions[this.name]
  274. resolve()
  275. }
  276. })
  277. })
  278. }
  279. }
  280. class WriteStream {
  281. id : string;
  282. encoding : string;
  283. append : bool;
  284. constructor(streamId:string, encoding:string, append:string) {
  285. this.id = streamId
  286. this.encoding = encoding
  287. this.append = append
  288. }
  289. write(data:string) {
  290. return new Promise((resolve, reject) => {
  291. try {
  292. let method = this.encoding === 'ascii' ? 'writeArrayChunk' : 'writeChunk'
  293. if(this.encoding.toLocaleLowerCase() === 'ascii' && !Array.isArray(data)) {
  294. reject('ascii input data must be an Array')
  295. return
  296. }
  297. RNFetchBlob[method](this.id, data, (error) => {
  298. if(error)
  299. reject(error)
  300. else
  301. resolve()
  302. })
  303. } catch(err) {
  304. reject(err)
  305. }
  306. })
  307. }
  308. close() {
  309. return new Promise((resolve, reject) => {
  310. try {
  311. RNFetchBlob.closeStream(this.id, () => {
  312. resolve()
  313. })
  314. } catch (err) {
  315. reject(err)
  316. }
  317. })
  318. }
  319. }
  320. export default {
  321. RNFetchBlobSession,
  322. unlink,
  323. mkdir,
  324. session,
  325. ls,
  326. readStream,
  327. getSystemDirs,
  328. mv,
  329. cp,
  330. writeStream,
  331. exists,
  332. createFile,
  333. isDir
  334. }