BlobManager.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @flow strict-local
  8. * @format
  9. */
  10. 'use strict';
  11. const Blob = require('./Blob');
  12. const BlobRegistry = require('./BlobRegistry');
  13. import type {BlobData, BlobOptions, BlobCollector} from './BlobTypes';
  14. import NativeBlobModule from './NativeBlobModule';
  15. import invariant from 'invariant';
  16. /*eslint-disable no-bitwise */
  17. /*eslint-disable eqeqeq */
  18. /**
  19. * Based on the rfc4122-compliant solution posted at
  20. * http://stackoverflow.com/questions/105034
  21. */
  22. function uuidv4(): string {
  23. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
  24. const r = (Math.random() * 16) | 0,
  25. v = c == 'x' ? r : (r & 0x3) | 0x8;
  26. return v.toString(16);
  27. });
  28. }
  29. // **Temporary workaround**
  30. // TODO(#24654): Use turbomodules for the Blob module.
  31. // Blob collector is a jsi::HostObject that is used by native to know
  32. // when the a Blob instance is deallocated. This allows to free the
  33. // underlying native resources. This is a hack to workaround the fact
  34. // that the current bridge infra doesn't allow to track js objects
  35. // deallocation. Ideally the whole Blob object should be a jsi::HostObject.
  36. function createBlobCollector(blobId: string): BlobCollector | null {
  37. if (global.__blobCollectorProvider == null) {
  38. return null;
  39. } else {
  40. return global.__blobCollectorProvider(blobId);
  41. }
  42. }
  43. /**
  44. * Module to manage blobs. Wrapper around the native blob module.
  45. */
  46. class BlobManager {
  47. /**
  48. * If the native blob module is available.
  49. */
  50. static isAvailable: boolean = !!NativeBlobModule;
  51. /**
  52. * Create blob from existing array of blobs.
  53. */
  54. static createFromParts(
  55. parts: Array<Blob | string>,
  56. options?: BlobOptions,
  57. ): Blob {
  58. invariant(NativeBlobModule, 'NativeBlobModule is available.');
  59. const blobId = uuidv4();
  60. const items = parts.map(part => {
  61. if (
  62. part instanceof ArrayBuffer ||
  63. (global.ArrayBufferView && part instanceof global.ArrayBufferView)
  64. ) {
  65. throw new Error(
  66. "Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported",
  67. );
  68. }
  69. if (part instanceof Blob) {
  70. return {
  71. data: part.data,
  72. type: 'blob',
  73. };
  74. } else {
  75. return {
  76. data: String(part),
  77. type: 'string',
  78. };
  79. }
  80. });
  81. const size = items.reduce((acc, curr) => {
  82. if (curr.type === 'string') {
  83. return acc + global.unescape(encodeURI(curr.data)).length;
  84. } else {
  85. return acc + curr.data.size;
  86. }
  87. }, 0);
  88. NativeBlobModule.createFromParts(items, blobId);
  89. return BlobManager.createFromOptions({
  90. blobId,
  91. offset: 0,
  92. size,
  93. type: options ? options.type : '',
  94. lastModified: options ? options.lastModified : Date.now(),
  95. });
  96. }
  97. /**
  98. * Create blob instance from blob data from native.
  99. * Used internally by modules like XHR, WebSocket, etc.
  100. */
  101. static createFromOptions(options: BlobData): Blob {
  102. BlobRegistry.register(options.blobId);
  103. return Object.assign(Object.create(Blob.prototype), {
  104. data:
  105. // Reuse the collector instance when creating from an existing blob.
  106. // This will make sure that the underlying resource is only deallocated
  107. // when all blobs that refer to it are deallocated.
  108. options.__collector == null
  109. ? {
  110. ...options,
  111. __collector: createBlobCollector(options.blobId),
  112. }
  113. : options,
  114. });
  115. }
  116. /**
  117. * Deallocate resources for a blob.
  118. */
  119. static release(blobId: string): void {
  120. invariant(NativeBlobModule, 'NativeBlobModule is available.');
  121. BlobRegistry.unregister(blobId);
  122. if (BlobRegistry.has(blobId)) {
  123. return;
  124. }
  125. NativeBlobModule.release(blobId);
  126. }
  127. /**
  128. * Inject the blob content handler in the networking module to support blob
  129. * requests and responses.
  130. */
  131. static addNetworkingHandler(): void {
  132. invariant(NativeBlobModule, 'NativeBlobModule is available.');
  133. NativeBlobModule.addNetworkingHandler();
  134. }
  135. /**
  136. * Indicate the websocket should return a blob for incoming binary
  137. * messages.
  138. */
  139. static addWebSocketHandler(socketId: number): void {
  140. invariant(NativeBlobModule, 'NativeBlobModule is available.');
  141. NativeBlobModule.addWebSocketHandler(socketId);
  142. }
  143. /**
  144. * Indicate the websocket should no longer return a blob for incoming
  145. * binary messages.
  146. */
  147. static removeWebSocketHandler(socketId: number): void {
  148. invariant(NativeBlobModule, 'NativeBlobModule is available.');
  149. NativeBlobModule.removeWebSocketHandler(socketId);
  150. }
  151. /**
  152. * Send a blob message to a websocket.
  153. */
  154. static sendOverSocket(blob: Blob, socketId: number): void {
  155. invariant(NativeBlobModule, 'NativeBlobModule is available.');
  156. NativeBlobModule.sendOverSocket(blob.data, socketId);
  157. }
  158. }
  159. module.exports = BlobManager;