@samitouri / QOSami-HFS / commits / 05252d26

fix: upload: temp file is lost if user refuses to resume and then upload is interrupted

Massimo Melina committed Sep 27, 2024 at 14:08 UTC 05252d265d19f95ef9ab09e77c85aaadb4a63c6f
1 file changed +9 -6
src/upload.ts
+9 -6
@@ -96,7 +96,7 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
96 // use temporary name while uploading
97 const keepName = basename(fullPath).slice(-200)
98 let tempName = join(dir, 'hfs$upload-' + keepName)
99 - const resumable = fs.existsSync(tempName) && !openFiles.has(tempName) && tempName
99 + const resumable = fs.existsSync(tempName) && !openFiles.has(tempName) && tempName // resumable is temp-file-1
100 if (resumable)
101 tempName = join(dir, 'hfs$upload2-' + keepName)
102 // checks for resume feature
@@ -107,13 +107,15 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
107 if (resume > size)
108 return fail(HTTP_RANGE_NOT_SATISFIABLE)
109 // warn frontend about resume possibility
110 + let resumableLost = false
111 if (!resume && resumable) {
112 const timeout = 30
113 notifyClient(ctx, 'upload.resumable', { [path]: size, expires: Date.now() + timeout * 1000 })
113 - delayedDelete(resumable, timeout, () =>
114 - fs.rename(tempName, resumable, err => {
115 - if (!err)
116 - tempName = resumable
114 + delayedDelete(resumable, timeout, () => // if user resumes, this upload is interrupted, and next upload will cancel this delayedDelete
115 + fs.rename(tempName, resumable, err => { // try to rename upload2 to upload, overwriting
116 + if (err) return
117 + tempName = resumable
118 + resumableLost = true
119 }) )
120 }
121 // append if resuming
@@ -142,7 +144,7 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
144 writeStream.once('close', async () => {
145 try {
146 if (ctx.req.aborted) {
145 - if (resumable && !resuming) // we don't want to be left with 2 temp files
147 + if (resumable && !resumableLost && !resuming) // we don't want to be left with 2 temp files
148 return delayedDelete(tempName, 0)
149 const sec = deleteUnfinishedUploadsAfter.get()
150 return _.isNumber(sec) && delayedDelete(tempName, sec)
@@ -157,6 +159,7 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
159 }
160 try {
161 await rename(tempName, dest)
162 + cancelDeletion(tempName) // not necessary, as deletion's failure is silent, but still
163 ctx.state.uploadDestinationPath = dest
164 setUploadMeta(dest, ctx)
165 if (ctx.query.comment)