説明なし

Blob.js 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // Copyright 2016 wkh237@github. All rights reserved.
  2. // Use of this source code is governed by a MIT-style license that can be
  3. // found in the LICENSE file.
  4. import RNFetchBlob from '../index.js'
  5. import fs from '../fs.js'
  6. import getUUID from '../utils/uuid'
  7. import Log from '../utils/log.js'
  8. import EventTarget from './EventTarget'
  9. const log = new Log('Blob')
  10. const blobCacheDir = fs.dirs.DocumentDir + '/RNFetchBlob-blobs/'
  11. log.level(3)
  12. /**
  13. * A RNFetchBlob style Blob polyfill class, this is a Blob which compatible to
  14. * Response object attain fron RNFetchBlob.fetch.
  15. */
  16. export default class Blob extends EventTarget {
  17. cacheName:string;
  18. type:string;
  19. size:number;
  20. isRNFetchBlobPolyfill:boolean = true;
  21. _ref:string = null;
  22. _blobCreated:boolean = false;
  23. _onCreated:Array<any> = [];
  24. /**
  25. * Static method that remove all files in Blob cache folder.
  26. * @nonstandard
  27. * @return {Promise}
  28. */
  29. static clearCache() {
  30. return fs.unlink(blobCacheDir).then(() => fs.mkdir(blobCacheDir))
  31. }
  32. /**
  33. * RNFetchBlob Blob polyfill, create a Blob directly from file path, BASE64
  34. * encoded data, and string. The conversion is done implicitly according to
  35. * given `mime`. However, the blob creation is asynchronously, to register
  36. * event `onCreated` is need to ensure the Blob is creadted.
  37. * @param {any} data Content of Blob object
  38. * @param {any} mime Content type settings of Blob object, `text/plain`
  39. * by default
  40. */
  41. constructor(data:any, cType:any) {
  42. super()
  43. cType = cType || {}
  44. this.cacheName = getBlobName()
  45. this.isRNFetchBlobPolyfill = true
  46. this.type = cType.type || 'text/plain'
  47. log.verbose('Blob constructor called', 'mime', this.type, 'type', typeof data, 'length', data.length)
  48. this._ref = blobCacheDir + this.cacheName
  49. let p = null
  50. if(data instanceof Blob) {
  51. log.verbose('create Blob cache file from Blob object')
  52. let size = 0
  53. this._ref = String(data.getRNFetchBlobRef())
  54. let orgPath = this._ref
  55. p = fs.exists(orgPath)
  56. .then((exist) => {
  57. if(exist)
  58. return fs.writeFile(orgPath, data, 'uri')
  59. .then((size) => Promise.resolve(size))
  60. .catch((err) => {
  61. throw `RNFetchBlob Blob file creation error, ${err}`
  62. })
  63. else
  64. throw `could not create Blob from path ${orgPath}, file not exists`
  65. })
  66. }
  67. // if the data is a string starts with `RNFetchBlob-file://`, append the
  68. // Blob data from file path
  69. else if(typeof data === 'string' && data.startsWith('RNFetchBlob-file://')) {
  70. log.verbose('create Blob cache file from file path', data)
  71. this._ref = String(data).replace('RNFetchBlob-file://', '')
  72. let orgPath = this._ref
  73. p = fs.stat(orgPath)
  74. .then((stat) => {
  75. return Promise.resolve(stat.size)
  76. })
  77. }
  78. // content from variable need create file
  79. else if(typeof data === 'string') {
  80. let encoding = 'utf8'
  81. let mime = String(this.type)
  82. // when content type contains application/octet* or *;base64, RNFetchBlob
  83. // fs will treat it as BASE64 encoded string binary data
  84. if(/(application\/octet|\;base64)/i.test(mime))
  85. encoding = 'base64'
  86. else
  87. data = data.toString()
  88. // create cache file
  89. log.verbose('create Blob cache file from string', 'encode', encoding)
  90. p = fs.writeFile(this._ref, data, encoding)
  91. .then((size) => Promise.resolve(size))
  92. }
  93. // TODO : ArrayBuffer support
  94. // else if (data instanceof ArrayBuffer ) {
  95. //
  96. // }
  97. // when input is an array of mixed data types, create a file cache
  98. else if(Array.isArray(data)) {
  99. log.verbose('create Blob cache file from mixed array', data)
  100. p = createMixedBlobData(this._ref, data)
  101. }
  102. else {
  103. data = data.toString()
  104. p = fs.writeFile(this._ref, data, 'utf8')
  105. .then((size) => Promise.resolve(size))
  106. }
  107. p && p.then((size) => {
  108. this.size = size
  109. this._invokeOnCreateEvent()
  110. })
  111. .catch((err) => {
  112. log.error('RNFetchBlob could not create Blob : '+ this._ref, err)
  113. })
  114. }
  115. /**
  116. * Since Blob content will asynchronously write to a file during creation,
  117. * use this method to register an event handler for Blob initialized event.
  118. * @nonstandard
  119. * @param {[type]} fn:( [description]
  120. * @return {[type]} [description]
  121. */
  122. onCreated(fn:() => void) {
  123. log.verbose('register blob onCreated', this._onCreated.length)
  124. if(!this._blobCreated)
  125. this._onCreated.push(fn)
  126. else
  127. fn(this)
  128. }
  129. /**
  130. * Get file reference of the Blob object.
  131. * @nonstandard
  132. * @return {string} Blob file reference which can be consumed by RNFetchBlob fs
  133. */
  134. getRNFetchBlobRef() {
  135. return this._ref
  136. }
  137. /**
  138. * Create a Blob object which is sliced from current object
  139. * @param {number} start Start byte number
  140. * @param {number} end End byte number
  141. * @param {string} contentType Optional, content type of new Blob object
  142. * @return {Blob}
  143. */
  144. slice(start:?number, end:?number, encoding:?string):Blob {
  145. log.verbose('slice called')
  146. // TODO : fs.slice
  147. // return fs.slice(this.cacheName, getBlobName(), contentType, start, end)
  148. }
  149. /**
  150. * Release the resource of the Blob object.
  151. * @nonstandard
  152. * @return {Promise}
  153. */
  154. close() {
  155. return fs.unlink(this._ref)
  156. }
  157. _invokeOnCreateEvent() {
  158. log.verbose('invoke create event')
  159. this._blobCreated = true
  160. let fns = this._onCreated
  161. for(let i in fns) {
  162. if(typeof fns[i] === 'function')
  163. fns[i](this)
  164. }
  165. delete this._onCreated
  166. }
  167. }
  168. /**
  169. * Get a temp filename for Blob object
  170. * @return {string} Temporary filename
  171. */
  172. function getBlobName() {
  173. return 'blob-' + getUUID()
  174. }
  175. /**
  176. * Create a file according to given array. The element in array can be a number,
  177. * Blob, String, Array.
  178. * @param {string} ref File path reference
  179. * @param {Array} dataArray An array contains different types of data.
  180. * @return {Promise}
  181. */
  182. function createMixedBlobData(ref, dataArray) {
  183. let p = fs.writeFile(ref, '')
  184. let args = []
  185. let size = 0
  186. for(let i in dataArray) {
  187. let part = dataArray[i]
  188. if(part instanceof Blob)
  189. args.push([ref, part.getRNFetchBlobRef(), 'uri'])
  190. else if(typeof part === 'string')
  191. args.push([ref, part, 'utf8'])
  192. // TODO : ArrayBuffer
  193. // else if (part instanceof ArrayBuffer) {
  194. //
  195. // }
  196. else if (Array.isArray(part))
  197. args.push([ref, part, 'ascii'])
  198. }
  199. return p.then(() => {
  200. let promises = args.map((p) => {
  201. log.verbose('mixed blob write', ...p)
  202. return fs.appendFile.call(this, ...p)
  203. })
  204. return Promise.all(promises).then((sizes) => {
  205. console.log('blob write size', sizes)
  206. for(let i in sizes) {
  207. size += sizes[i]
  208. }
  209. return Promise.resolve(size)
  210. })
  211. })
  212. }