triflix commited on
Commit
788bfc4
·
verified ·
1 Parent(s): 491d30d

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +187 -0
templates/index.html ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>File Uploader</title>
7
+ <!-- Bootstrap CSS -->
8
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
9
+ <style>
10
+ body {
11
+ padding-top: 40px;
12
+ padding-bottom: 40px;
13
+ background-color: #f5f5f5;
14
+ }
15
+ .container {
16
+ max-width: 600px;
17
+ }
18
+ .upload-form {
19
+ background-color: #fff;
20
+ padding: 30px;
21
+ border-radius: 8px;
22
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
23
+ }
24
+ .progress-container {
25
+ margin-top: 20px;
26
+ display: none; /* Hidden by default */
27
+ }
28
+ #uploadInfo {
29
+ margin-top: 10px;
30
+ font-size: 0.9em;
31
+ }
32
+ #downloadLinkContainer {
33
+ margin-top: 20px;
34
+ display: none; /* Hidden by default */
35
+ }
36
+ .loader {
37
+ border: 5px solid #f3f3f3; /* Light grey */
38
+ border-top: 5px solid #3498db; /* Blue */
39
+ border-radius: 50%;
40
+ width: 30px;
41
+ height: 30px;
42
+ animation: spin 1s linear infinite;
43
+ display: none; /* Hidden by default */
44
+ margin: 10px auto;
45
+ }
46
+ @keyframes spin {
47
+ 0% { transform: rotate(0deg); }
48
+ 100% { transform: rotate(360deg); }
49
+ }
50
+ </style>
51
+ </head>
52
+ <body>
53
+ <div class="container">
54
+ <div class="upload-form">
55
+ <h2 class="text-center mb-4">Upload a File</h2>
56
+ <form id="uploadForm" enctype="multipart/form-data">
57
+ <div class="mb-3">
58
+ <label for="fileInput" class="form-label">Choose file</label>
59
+ <input class="form-control" type="file" id="fileInput" name="file" required>
60
+ </div>
61
+ <button type="submit" class="btn btn-primary w-100">Upload</button>
62
+ </form>
63
+
64
+ <div class="loader" id="loader"></div>
65
+
66
+ <div class="progress-container" id="progressContainer">
67
+ <div class="progress">
68
+ <div id="progressBar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
69
+ </div>
70
+ <div id="uploadInfo" class="text-muted"></div>
71
+ </div>
72
+
73
+ <div id="downloadLinkContainer" class="alert alert-success" role="alert">
74
+ <strong>Success!</strong> File uploaded. <br>
75
+ Download link: <a href="#" id="downloadLink" target="_blank"></a>
76
+ </div>
77
+
78
+ <div id="errorContainer" class="alert alert-danger mt-3" role="alert" style="display: none;">
79
+ <strong>Error:</strong> <span id="errorMessage"></span>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <!-- Bootstrap JS Bundle (Popper.js included) -->
85
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
86
+ <script>
87
+ const uploadForm = document.getElementById('uploadForm');
88
+ const fileInput = document.getElementById('fileInput');
89
+ const loader = document.getElementById('loader');
90
+ const progressContainer = document.getElementById('progressContainer');
91
+ const progressBar = document.getElementById('progressBar');
92
+ const uploadInfo = document.getElementById('uploadInfo');
93
+ const downloadLinkContainer = document.getElementById('downloadLinkContainer');
94
+ const downloadLink = document.getElementById('downloadLink');
95
+ const errorContainer = document.getElementById('errorContainer');
96
+ const errorMessage = document.getElementById('errorMessage');
97
+
98
+ let startTime;
99
+
100
+ uploadForm.addEventListener('submit', async function(event) {
101
+ event.preventDefault();
102
+
103
+ const file = fileInput.files[0];
104
+ if (!file) {
105
+ alert('Please select a file to upload.');
106
+ return;
107
+ }
108
+
109
+ const formData = new FormData();
110
+ formData.append('file', file);
111
+
112
+ // Reset UI
113
+ loader.style.display = 'block';
114
+ progressContainer.style.display = 'block';
115
+ progressBar.style.width = '0%';
116
+ progressBar.textContent = '0%';
117
+ uploadInfo.textContent = 'Starting upload...';
118
+ downloadLinkContainer.style.display = 'none';
119
+ errorContainer.style.display = 'none';
120
+
121
+ startTime = Date.now();
122
+
123
+ const xhr = new XMLHttpRequest();
124
+ xhr.open('POST', '/upload/', true);
125
+
126
+ xhr.upload.onprogress = function(event) {
127
+ if (event.lengthComputable) {
128
+ const percentComplete = Math.round((event.loaded / event.total) * 100);
129
+ progressBar.style.width = percentComplete + '%';
130
+ progressBar.textContent = percentComplete + '%';
131
+
132
+ const elapsedTime = (Date.now() - startTime) / 1000; // seconds
133
+ const speed = event.loaded / elapsedTime; // bytes per second
134
+ const speedMbps = (speed * 8 / 1000000).toFixed(2); // Mbps
135
+
136
+ uploadInfo.textContent = `Uploaded ${formatBytes(event.loaded)} of ${formatBytes(event.total)} (${percentComplete}%) at ${speedMbps} Mbps`;
137
+ }
138
+ };
139
+
140
+ xhr.onload = function() {
141
+ loader.style.display = 'none';
142
+ if (xhr.status === 200) {
143
+ const response = JSON.parse(xhr.responseText);
144
+ uploadInfo.textContent = 'Upload complete!';
145
+ progressBar.classList.remove('progress-bar-animated');
146
+ progressBar.classList.add('bg-success');
147
+ downloadLink.href = response.download_url;
148
+ // The filename in the link text should be the one returned by the server (sanitized)
149
+ downloadLink.textContent = response.filename;
150
+ downloadLinkContainer.style.display = 'block';
151
+ } else {
152
+ progressBar.classList.remove('progress-bar-animated');
153
+ progressBar.classList.add('bg-danger');
154
+ try {
155
+ const errorResponse = JSON.parse(xhr.responseText);
156
+ errorMessage.textContent = errorResponse.detail || `Server error: ${xhr.status}`;
157
+ } catch (e) {
158
+ errorMessage.textContent = `Server error: ${xhr.status} - ${xhr.statusText}`;
159
+ }
160
+ errorContainer.style.display = 'block';
161
+ uploadInfo.textContent = 'Upload failed.';
162
+ }
163
+ };
164
+
165
+ xhr.onerror = function() {
166
+ loader.style.display = 'none';
167
+ progressBar.classList.remove('progress-bar-animated');
168
+ progressBar.classList.add('bg-danger');
169
+ errorMessage.textContent = 'Network error or server unavailable.';
170
+ errorContainer.style.display = 'block';
171
+ uploadInfo.textContent = 'Upload failed.';
172
+ };
173
+
174
+ xhr.send(formData);
175
+ });
176
+
177
+ function formatBytes(bytes, decimals = 2) {
178
+ if (bytes === 0) return '0 Bytes';
179
+ const k = 1024;
180
+ const dm = decimals < 0 ? 0 : decimals;
181
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
182
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
183
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
184
+ }
185
+ </script>
186
+ </body>
187
+ </html>