Skip to content

Commit

Permalink
fix(react-native): prevent app crashes while multipart uploading (#308)
Browse files Browse the repository at this point in the history
  • Loading branch information
nd0ut authored Mar 16, 2021
1 parent e05cd48 commit 5d305e6
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 27 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"react-native": {
"./lib/request/request.node.js": "./lib/request/request.browser.js",
"./lib/tools/getFormData.node.js": "./lib/tools/getFormData.react-native.js",
"./lib/tools/sockets.node.js": "./lib/tools/sockets.browser.js"
"./lib/tools/sockets.node.js": "./lib/tools/sockets.browser.js",
"./lib/uploadFile/prepareChunks.node.js": "./lib/uploadFile/prepareChunks.react-native.js"
},
"scripts": {
"check-env-vars": "node ./checkvars.js",
Expand Down
10 changes: 10 additions & 0 deletions src/uploadFile/prepareChunks.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { sliceChunk } from './sliceChunk'

export function prepareChunks(
file: Buffer | Blob,
fileSize: number,
chunkSize: number
): (index: number) => Buffer | Blob {
return (index: number): Buffer | Blob =>
sliceChunk(file, index, fileSize, chunkSize)
}
24 changes: 24 additions & 0 deletions src/uploadFile/prepareChunks.react-native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { sliceChunk } from './sliceChunk'

/**
* React-native hack for blob slicing
*
* We need to store references to sliced blobs to prevent source blob from
* being deallocated until uploading complete. Access to deallocated blob
* causes app crash.
*
* See https://github.com/uploadcare/uploadcare-upload-client/issues/306
* and https://github.com/facebook/react-native/issues/27543
*/
export function prepareChunks(
file: Buffer | Blob,
fileSize: number,
chunkSize: number
): (index: number) => Buffer | Blob {
const chunks: (Buffer | Blob)[] = []
return (index: number): Buffer | Blob => {
const chunk = sliceChunk(file, index, fileSize, chunkSize)
chunks.push(chunk)
return chunk
}
}
11 changes: 11 additions & 0 deletions src/uploadFile/sliceChunk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const sliceChunk = (
file: Buffer | Blob,
index: number,
fileSize: number,
chunkSize: number
): Buffer | Blob => {
const start = chunkSize * index
const end = Math.min(start + chunkSize, fileSize)

return file.slice(start, end)
}
38 changes: 12 additions & 26 deletions src/uploadFile/uploadMultipart.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import defaultSettings from '../defaultSettings'
import { prepareChunks } from './prepareChunks.node'
import multipartStart from '../api/multipartStart'
import multipartUpload from '../api/multipartUpload'
import multipartComplete from '../api/multipartComplete'
Expand Down Expand Up @@ -38,18 +39,6 @@ export type MultipartOptions = {
baseCDN?: string
}

const getChunk = (
file: Buffer | Blob,
index: number,
fileSize: number,
chunkSize: number
): Buffer | Blob => {
const start = chunkSize * index
const end = Math.min(start + chunkSize, fileSize)

return file.slice(start, end)
}

const uploadPartWithRetry = (
chunk: Buffer | Blob,
url: string,
Expand Down Expand Up @@ -138,27 +127,24 @@ const uploadMultipart = (
integration,
retryThrottledRequestMaxTimes
})
.then(({ uuid, parts }) =>
Promise.all([
.then(({ uuid, parts }) => {
const getChunk = prepareChunks(file, size, multipartChunkSize)
return Promise.all([
uuid,
runWithConcurrency(
maxConcurrentRequests,
parts.map((url, index) => (): Promise<MultipartUploadResponse> =>
uploadPartWithRetry(
getChunk(file, index, size, multipartChunkSize),
url,
{
publicKey,
onProgress: createProgressHandler(parts.length, index),
cancel,
integration,
multipartMaxAttempts
}
)
uploadPartWithRetry(getChunk(index), url, {
publicKey,
onProgress: createProgressHandler(parts.length, index),
cancel,
integration,
multipartMaxAttempts
})
)
)
])
)
})
.then(([uuid]) =>
multipartComplete(uuid, {
publicKey,
Expand Down

0 comments on commit 5d305e6

Please sign in to comment.