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