Skip to content

Commit 606ba30

Browse files
authored
Upload progress accounts for server processing time (rails#55157)
Progress bars now reflect the full upload lifecycle: 90% for request transmission, 10% for server processing and response. Prevents progress bars from completing prematurely while the server is still processing buffered uploads, giving the impression that something has failed or stalled. Progress is reported in two phases: - 0-90%: Request transmission. Actual upload progress. - 90-100%: Server processing time, estimated based on file size: - 1s for <1MB; 2s for 1-10MB; 3s+ for larger files The 90/10 split reflects that most direct uploads are unbuffered and complete quickly after transmission. Even buffered uploads typically process faster than their upload time. No app changes required for apps using Active Storage or Action Text. Progress events dispatch with the same `{ progress: number }` format. Fixes rails#42228
1 parent a0756eb commit 606ba30

File tree

9 files changed

+424
-151
lines changed

9 files changed

+424
-151
lines changed

actiontext/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Attachment upload progress accounts for server processing time.
2+
3+
*Jeremy Daer*
4+
15
* The Trix dependency is now satisfied by a gem, `action_text-trix`, rather than vendored
26
files. This allows applications to bump Trix versions independently of Rails
37
releases. Effectively this also upgrades Trix to `>= 2.1.15`.

actiontext/app/assets/javascripts/actiontext.esm.js

Lines changed: 76 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

actiontext/app/assets/javascripts/actiontext.js

Lines changed: 76 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

actiontext/app/javascript/actiontext/attachment_upload.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,61 @@ export class AttachmentUpload {
1414

1515
directUploadWillStoreFileWithXHR(xhr) {
1616
xhr.upload.addEventListener("progress", event => {
17-
const progress = event.loaded / event.total * 100
17+
// Scale upload progress to 0-90% range
18+
const progress = (event.loaded / event.total) * 90
1819
this.attachment.setUploadProgress(progress)
1920
if (progress) {
2021
this.dispatch("progress", { progress: progress })
2122
}
2223
})
24+
25+
// Start simulating progress after upload completes
26+
xhr.upload.addEventListener("loadend", () => {
27+
this.simulateResponseProgress(xhr)
28+
})
29+
}
30+
31+
simulateResponseProgress(xhr) {
32+
let progress = 90
33+
const startTime = Date.now()
34+
35+
const updateProgress = () => {
36+
// Simulate progress from 90% to 99% over estimated time
37+
const elapsed = Date.now() - startTime
38+
const estimatedResponseTime = this.estimateResponseTime()
39+
const responseProgress = Math.min(elapsed / estimatedResponseTime, 1)
40+
progress = 90 + (responseProgress * 9) // 90% to 99%
41+
42+
this.attachment.setUploadProgress(progress)
43+
this.dispatch("progress", { progress })
44+
45+
// Continue until response arrives or we hit 99%
46+
if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
47+
requestAnimationFrame(updateProgress)
48+
}
49+
}
50+
51+
// Stop simulation when response arrives
52+
xhr.addEventListener("loadend", () => {
53+
this.attachment.setUploadProgress(100)
54+
this.dispatch("progress", { progress: 100 })
55+
})
56+
57+
requestAnimationFrame(updateProgress)
58+
}
59+
60+
estimateResponseTime() {
61+
// Base estimate: 1 second for small files, scaling up for larger files
62+
const fileSize = this.attachment.file.size
63+
const MB = 1024 * 1024
64+
65+
if (fileSize < MB) {
66+
return 1000 // 1 second for files under 1MB
67+
} else if (fileSize < 10 * MB) {
68+
return 2000 // 2 seconds for files 1-10MB
69+
} else {
70+
return 3000 + (fileSize / MB * 50) // 3+ seconds for larger files
71+
}
2372
}
2473

2574
directUploadDidComplete(error, attributes) {

activestorage/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Direct upload progress accounts for server processing time.
2+
3+
*Jeremy Daer*
4+
15
* Delegate `ActiveStorage::Filename#to_str` to `#to_s`
26

37
Supports checking String equality:

activestorage/app/assets/javascripts/activestorage.esm.js

Lines changed: 37 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

activestorage/app/assets/javascripts/activestorage.js

Lines changed: 37 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)