@rails/activestorage 8.0.201 → 8.1.0-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Active Storage
|
|
2
2
|
|
|
3
|
-
Active Storage makes it simple to upload and reference files in cloud services like [Amazon S3](https://aws.amazon.com/s3/), [Google Cloud Storage](https://cloud.google.com/storage/docs/),
|
|
3
|
+
Active Storage makes it simple to upload and reference files in cloud services like [Amazon S3](https://aws.amazon.com/s3/), or [Google Cloud Storage](https://cloud.google.com/storage/docs/), and attach those files to Active Records. Supports having one main service and mirrors in other services for redundancy. It also provides a disk service for testing or local deployments, but the focus is on cloud storage.
|
|
4
4
|
|
|
5
5
|
Files can be uploaded from the server to the cloud or directly from the client to the cloud.
|
|
6
6
|
|
|
@@ -173,7 +173,10 @@ Active Storage, with its included JavaScript library, supports uploading directl
|
|
|
173
173
|
```erb
|
|
174
174
|
<%= form.file_field :attachments, multiple: true, direct_upload: true %>
|
|
175
175
|
```
|
|
176
|
-
|
|
176
|
+
|
|
177
|
+
3. Configure CORS on third-party storage services to allow direct upload requests.
|
|
178
|
+
|
|
179
|
+
4. That's it! Uploads begin upon form submission.
|
|
177
180
|
|
|
178
181
|
### Direct upload JavaScript events
|
|
179
182
|
|
|
@@ -203,6 +206,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
|
|
|
203
206
|
|
|
204
207
|
* https://github.com/rails/rails/issues
|
|
205
208
|
|
|
206
|
-
Feature requests should be discussed on the
|
|
209
|
+
Feature requests should be discussed on the rubyonrails-core forum here:
|
|
207
210
|
|
|
208
211
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
|
@@ -672,7 +672,7 @@ class DirectUploadController {
|
|
|
672
672
|
}));
|
|
673
673
|
}
|
|
674
674
|
uploadRequestDidProgress(event) {
|
|
675
|
-
const progress = event.loaded / event.total *
|
|
675
|
+
const progress = event.loaded / event.total * 90;
|
|
676
676
|
if (progress) {
|
|
677
677
|
this.dispatch("progress", {
|
|
678
678
|
progress: progress
|
|
@@ -707,6 +707,42 @@ class DirectUploadController {
|
|
|
707
707
|
xhr: xhr
|
|
708
708
|
});
|
|
709
709
|
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
|
710
|
+
xhr.upload.addEventListener("loadend", (() => {
|
|
711
|
+
this.simulateResponseProgress(xhr);
|
|
712
|
+
}));
|
|
713
|
+
}
|
|
714
|
+
simulateResponseProgress(xhr) {
|
|
715
|
+
let progress = 90;
|
|
716
|
+
const startTime = Date.now();
|
|
717
|
+
const updateProgress = () => {
|
|
718
|
+
const elapsed = Date.now() - startTime;
|
|
719
|
+
const estimatedResponseTime = this.estimateResponseTime();
|
|
720
|
+
const responseProgress = Math.min(elapsed / estimatedResponseTime, 1);
|
|
721
|
+
progress = 90 + responseProgress * 9;
|
|
722
|
+
this.dispatch("progress", {
|
|
723
|
+
progress: progress
|
|
724
|
+
});
|
|
725
|
+
if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
|
|
726
|
+
requestAnimationFrame(updateProgress);
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
xhr.addEventListener("loadend", (() => {
|
|
730
|
+
this.dispatch("progress", {
|
|
731
|
+
progress: 100
|
|
732
|
+
});
|
|
733
|
+
}));
|
|
734
|
+
requestAnimationFrame(updateProgress);
|
|
735
|
+
}
|
|
736
|
+
estimateResponseTime() {
|
|
737
|
+
const fileSize = this.file.size;
|
|
738
|
+
const MB = 1024 * 1024;
|
|
739
|
+
if (fileSize < MB) {
|
|
740
|
+
return 1e3;
|
|
741
|
+
} else if (fileSize < 10 * MB) {
|
|
742
|
+
return 2e3;
|
|
743
|
+
} else {
|
|
744
|
+
return 3e3 + fileSize / MB * 50;
|
|
745
|
+
}
|
|
710
746
|
}
|
|
711
747
|
}
|
|
712
748
|
|
|
@@ -662,7 +662,7 @@
|
|
|
662
662
|
}));
|
|
663
663
|
}
|
|
664
664
|
uploadRequestDidProgress(event) {
|
|
665
|
-
const progress = event.loaded / event.total *
|
|
665
|
+
const progress = event.loaded / event.total * 90;
|
|
666
666
|
if (progress) {
|
|
667
667
|
this.dispatch("progress", {
|
|
668
668
|
progress: progress
|
|
@@ -697,6 +697,42 @@
|
|
|
697
697
|
xhr: xhr
|
|
698
698
|
});
|
|
699
699
|
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
|
700
|
+
xhr.upload.addEventListener("loadend", (() => {
|
|
701
|
+
this.simulateResponseProgress(xhr);
|
|
702
|
+
}));
|
|
703
|
+
}
|
|
704
|
+
simulateResponseProgress(xhr) {
|
|
705
|
+
let progress = 90;
|
|
706
|
+
const startTime = Date.now();
|
|
707
|
+
const updateProgress = () => {
|
|
708
|
+
const elapsed = Date.now() - startTime;
|
|
709
|
+
const estimatedResponseTime = this.estimateResponseTime();
|
|
710
|
+
const responseProgress = Math.min(elapsed / estimatedResponseTime, 1);
|
|
711
|
+
progress = 90 + responseProgress * 9;
|
|
712
|
+
this.dispatch("progress", {
|
|
713
|
+
progress: progress
|
|
714
|
+
});
|
|
715
|
+
if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
|
|
716
|
+
requestAnimationFrame(updateProgress);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
xhr.addEventListener("loadend", (() => {
|
|
720
|
+
this.dispatch("progress", {
|
|
721
|
+
progress: 100
|
|
722
|
+
});
|
|
723
|
+
}));
|
|
724
|
+
requestAnimationFrame(updateProgress);
|
|
725
|
+
}
|
|
726
|
+
estimateResponseTime() {
|
|
727
|
+
const fileSize = this.file.size;
|
|
728
|
+
const MB = 1024 * 1024;
|
|
729
|
+
if (fileSize < MB) {
|
|
730
|
+
return 1e3;
|
|
731
|
+
} else if (fileSize < 10 * MB) {
|
|
732
|
+
return 2e3;
|
|
733
|
+
} else {
|
|
734
|
+
return 3e3 + fileSize / MB * 50;
|
|
735
|
+
}
|
|
700
736
|
}
|
|
701
737
|
}
|
|
702
738
|
const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rails/activestorage",
|
|
3
|
-
"version": "8.0
|
|
3
|
+
"version": "8.1.0-beta1",
|
|
4
4
|
"description": "Attach cloud and local files in Rails applications",
|
|
5
5
|
"module": "app/assets/javascripts/activestorage.esm.js",
|
|
6
6
|
"main": "app/assets/javascripts/activestorage.js",
|
|
@@ -31,7 +31,8 @@ export class DirectUploadController {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
uploadRequestDidProgress(event) {
|
|
34
|
-
|
|
34
|
+
// Scale upload progress to 0-90% range
|
|
35
|
+
const progress = (event.loaded / event.total) * 90
|
|
35
36
|
if (progress) {
|
|
36
37
|
this.dispatch("progress", { progress })
|
|
37
38
|
}
|
|
@@ -63,5 +64,51 @@ export class DirectUploadController {
|
|
|
63
64
|
directUploadWillStoreFileWithXHR(xhr) {
|
|
64
65
|
this.dispatch("before-storage-request", { xhr })
|
|
65
66
|
xhr.upload.addEventListener("progress", event => this.uploadRequestDidProgress(event))
|
|
67
|
+
|
|
68
|
+
// Start simulating progress after upload completes
|
|
69
|
+
xhr.upload.addEventListener("loadend", () => {
|
|
70
|
+
this.simulateResponseProgress(xhr)
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
simulateResponseProgress(xhr) {
|
|
75
|
+
let progress = 90
|
|
76
|
+
const startTime = Date.now()
|
|
77
|
+
|
|
78
|
+
const updateProgress = () => {
|
|
79
|
+
// Simulate progress from 90% to 99% over estimated time
|
|
80
|
+
const elapsed = Date.now() - startTime
|
|
81
|
+
const estimatedResponseTime = this.estimateResponseTime()
|
|
82
|
+
const responseProgress = Math.min(elapsed / estimatedResponseTime, 1)
|
|
83
|
+
progress = 90 + (responseProgress * 9) // 90% to 99%
|
|
84
|
+
|
|
85
|
+
this.dispatch("progress", { progress })
|
|
86
|
+
|
|
87
|
+
// Continue until response arrives or we hit 99%
|
|
88
|
+
if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
|
|
89
|
+
requestAnimationFrame(updateProgress)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Stop simulation when response arrives
|
|
94
|
+
xhr.addEventListener("loadend", () => {
|
|
95
|
+
this.dispatch("progress", { progress: 100 })
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
requestAnimationFrame(updateProgress)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
estimateResponseTime() {
|
|
102
|
+
// Base estimate: 1 second for small files, scaling up for larger files
|
|
103
|
+
const fileSize = this.file.size
|
|
104
|
+
const MB = 1024 * 1024
|
|
105
|
+
|
|
106
|
+
if (fileSize < MB) {
|
|
107
|
+
return 1000 // 1 second for files under 1MB
|
|
108
|
+
} else if (fileSize < 10 * MB) {
|
|
109
|
+
return 2000 // 2 seconds for files 1-10MB
|
|
110
|
+
} else {
|
|
111
|
+
return 3000 + (fileSize / MB * 50) // 3+ seconds for larger files
|
|
112
|
+
}
|
|
66
113
|
}
|
|
67
114
|
}
|