Skip to content

Commit

Permalink
feat: Added retry capability for uploading files
Browse files Browse the repository at this point in the history
Signed-off-by: Konstantin Myakshin <molodchick@gmail.com>
  • Loading branch information
Koc committed Jun 16, 2024
1 parent ea3591a commit a8edad0
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 10 deletions.
20 changes: 20 additions & 0 deletions __tests__/utils/upload.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ describe('Initialize chunks upload temporary workspace', () => {
expect(axiosMock.request).toHaveBeenCalledWith({
method: 'MKCOL',
url,
'axios-retry': {
retries: 5,
retryDelay: expect.any(Function),
},
})
})

Expand Down Expand Up @@ -105,6 +109,10 @@ describe('Initialize chunks upload temporary workspace', () => {
headers: {
Destination: 'https://cloud.domain.com/remote.php/dav/files/test/image.jpg',
},
'axios-retry': {
retries: 5,
retryDelay: expect.any(Function),
},
})
})
})
Expand All @@ -130,6 +138,10 @@ describe('Upload data', () => {
headers: {
'Content-Type': 'application/octet-stream',
},
'axios-retry': {
retries: 5,
retryDelay: expect.any(Function),
},
})
})
test('Upload async data stream', async () => {
Expand All @@ -155,6 +167,10 @@ describe('Upload data', () => {
headers: {
'Content-Type': 'application/octet-stream',
},
'axios-retry': {
retries: 5,
retryDelay: expect.any(Function),
},
})
})

Expand All @@ -179,6 +195,10 @@ describe('Upload data', () => {
Destination: url,
'Content-Type': 'application/octet-stream',
},
'axios-retry': {
retries: 5,
retryDelay: expect.any(Function),
},
})
})

Expand Down
5 changes: 0 additions & 5 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ const viteConfig = await createAppConfig({}, {
replace: {
__TRANSLATIONS__: '[]',
},
config: {
optimizeDeps: {
entries: ['cypress/**/*'],
},
},
})({ mode: 'development', command: 'serve' })

viteConfig.build!.rollupOptions = undefined
Expand Down
8 changes: 4 additions & 4 deletions lib/uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,9 @@ export class Uploader {
* @param {string} destination the destination path relative to the root folder. e.g. /foo/bar.txt
* @param {File|FileSystemFileEntry} fileHandle the file to upload
* @param {string} root the root folder to upload to
* @param retries number of retries
*/
upload(destination: string, fileHandle: File|FileSystemFileEntry, root?: string): PCancelable<Upload> {
upload(destination: string, fileHandle: File|FileSystemFileEntry, root?: string, retries: number = 5): PCancelable<Upload> {
root = root || this.root
const destinationPath = `${root.replace(/\/$/, '')}/${destination.replace(/^\//, '')}`

Expand Down Expand Up @@ -387,7 +388,7 @@ export class Uploader {
logger.debug('Initializing chunked upload', { file, upload })

// Let's initialize a chunk upload
const tempUrl = await initChunkWorkspace(encodedDestinationFile)
const tempUrl = await initChunkWorkspace(encodedDestinationFile, retries)
const chunksQueue: Array<Promise<void>> = []

// Generate chunks array
Expand All @@ -411,6 +412,7 @@ export class Uploader {
'OC-Total-Length': file.size,
'Content-Type': 'application/octet-stream',
},
retries,
)
// Update upload progress on chunk completion
.then(() => { upload.uploaded = upload.uploaded + maxChunkSize })
Expand All @@ -424,8 +426,6 @@ export class Uploader {

if (!isCancel(error)) {
logger.error(`Chunk ${chunk + 1} ${bufferStart} - ${bufferEnd} uploading failed`, { error, upload })
// TODO: support retrying ?
// https://github.com/nextcloud-libraries/nextcloud-upload/issues/5
upload.cancel()
upload.status = UploadStatus.FAILED
}
Expand Down
15 changes: 14 additions & 1 deletion lib/utils/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { AxiosProgressEvent, AxiosResponse } from 'axios'
import { generateRemoteUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import axios from '@nextcloud/axios'
import axiosRetry from 'axios-retry';
axiosRetry(axios, { retries: 0 });

type UploadData = Blob | (() => Promise<Blob>)

Expand All @@ -13,6 +15,7 @@ type UploadData = Blob | (() => Promise<Blob>)
* @param onUploadProgress the progress callback
* @param destinationFile the final destination file (often used for chunked uploads)
* @param headers additional headers
* @param retries number of retries
*/
export const uploadData = async function(
url: string,
Expand All @@ -21,6 +24,7 @@ export const uploadData = async function(
onUploadProgress:(event: AxiosProgressEvent) => void = () => {},
destinationFile: string | undefined = undefined,
headers: Record<string, string|number> = {},
retries: number = 5,
): Promise<AxiosResponse> {
let data: Blob

Expand Down Expand Up @@ -49,6 +53,10 @@ export const uploadData = async function(
signal,
onUploadProgress,
headers,
'axios-retry': {
retries,
retryDelay: (retryCount, error) => axiosRetry.exponentialDelay(retryCount, error, 1000),
},
})
}

Expand All @@ -70,8 +78,9 @@ export const getChunk = function(file: File, start: number, length: number): Pro
/**
* Create a temporary upload workspace to upload the chunks to
* @param destinationFile The file name after finishing the chunked upload
* @param retries number of retries
*/
export const initChunkWorkspace = async function(destinationFile: string | undefined = undefined): Promise<string> {
export const initChunkWorkspace = async function(destinationFile: string | undefined = undefined, retries: number = 5): Promise<string> {
const chunksWorkspace = generateRemoteUrl(`dav/uploads/${getCurrentUser()?.uid}`)
const hash = [...Array(16)].map(() => Math.floor(Math.random() * 16).toString(16)).join('')
const tempWorkspace = `web-file-upload-${hash}`
Expand All @@ -82,6 +91,10 @@ export const initChunkWorkspace = async function(destinationFile: string | undef
method: 'MKCOL',
url,
headers,
'axios-retry': {
retries,
retryDelay: (retryCount, error) => axiosRetry.exponentialDelay(retryCount, error, 1000),
},
})

return url
Expand Down
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"@nextcloud/paths": "^2.1.0",
"@nextcloud/router": "^3.0.0",
"axios": "^1.7.2",
"axios-retry": "^4.4.0",
"crypto-browserify": "^3.12.0",
"p-cancelable": "^4.0.1",
"p-queue": "^8.0.0",
Expand Down

0 comments on commit a8edad0

Please sign in to comment.