1class FileManager { 2 constructor(userId) { 3 this.currentPath = ''; 4 this.maxStorage = 128 * 1024 * 1024; // 128MB in bytes 5 this.usedStorage = 0; 6 this.files = []; 7 8 // DOM elements 9 this.pathInput = document.getElementById('pathInput'); 10 this.fileList = document.getElementById('fileList'); 11 this.storageProgress = document.getElementById('storageProgress'); 12 this.usedStorageText = document.getElementById('usedStorage'); 13 this.maxStorageText = document.getElementById('maxStorage'); 14 this.errorMessage = document.getElementById('errorMessage'); 15 this.backButton = document.getElementById('backButton'); 16 this.uploadButton = document.getElementById('uploadButton'); 17 this.fileInput = document.getElementById('fileInput'); 18 this.mkDirButton = document.getElementById('mkDirButton'); 19 20 // Bind events 21 this.pathInput.addEventListener('change', this.handlePathChange.bind(this)); 22 this.backButton.addEventListener('click', this.goBack.bind(this)); 23 this.uploadButton.addEventListener('click', () => this.fileInput.click()); 24 this.fileInput.addEventListener('change', this.handleFileUpload.bind(this)); 25 this.mkDirButton.addEventListener('click', this.handleMkDir.bind(this)); 26 27 // Check URL parameters 28 this.checkUrlParams(); 29 this.userId = userId ? userId : my.id; 30 // å®ä¾å çé æå¡å¨å®¢æ·ç«¯ 31 this.diskMirror = initDiskMirror(); 32 this.diskMirrorUpload = initUploadDiskMirror(); 33 } 34 35 checkUrlParams() { 36 const urlParams = new URLSearchParams(window.location.search); 37 const path = urlParams.get('path'); 38 if (path) { 39 this.currentPath = path; 40 this.pathInput.value = path; 41 this.loadFiles(); 42 } 43 } 44 45 handlePathChange(event) { 46 this.currentPath = event.target.value; 47 this.loadFiles(); 48 } 49 50 goBack() { 51 const strings = this.currentPath.split('/'); 52 if (strings.length > 1) { 53 this.currentPath = strings.slice(0, -1).join('/'); 54 } else { 55 this.currentPath = ''; 56 } 57 this.pathInput.value = this.currentPath; 58 this.loadFiles(); 59 } 60 61 formatSize(bytes) { 62 const units = ['B', 'KB', 'MB', 'GB']; 63 let size = bytes; 64 let unitIndex = 0; 65 66 while (size >= 1024 && unitIndex < units.length - 1) { 67 size /= 1024; 68 unitIndex++; 69 } 70 71 return `${size.toFixed(1)} ${units[unitIndex]}`; 72 } 73 74 updateStorageInfo() { 75 const percentage = (this.usedStorage / this.maxStorage) * 100; 76 this.storageProgress.style.width = `${percentage}%`; 77 this.usedStorageText.textContent = this.formatSize(this.usedStorage); 78 this.maxStorageText.textContent = this.formatSize(this.maxStorage); 79 } 80 81 createFileElement(file) { 82 const fileItem = document.createElement('div'); 83 fileItem.className = 'file-item'; 84 85 const fileIcon = document.createElement('div'); 86 fileIcon.className = 'file-icon'; 87 88 if (file.isDir) { 89 const img = document.createElement('img'); 90 img.src = '/image/folder_icon.png'; 91 img.alt = 'æä»¶å¤¹'; 92 fileIcon.appendChild(img); 93 } else if (file.fileName.match(/\.(jpg|jpeg|png|gif|webp)$/i)) { 94 const img = document.createElement('img'); 95 img.src = file.url; 96 img.alt = file.fileName; 97 fileIcon.appendChild(img); 98 } else { 99 fileIcon.textContent = 'ð'; 100 } 101 102 const fileName = document.createElement('div'); 103 fileName.className = 'file-name'; 104 fileName.textContent = file.fileName; 105 106 const fileSize = document.createElement('div'); 107 fileSize.className = 'file-size'; 108 fileSize.textContent = this.formatSize(file.size); 109 110 const fileActions = document.createElement('div'); 111 fileActions.className = 'file-actions'; 112 113 if (!file.isDir) { 114 const downloadBtn = document.createElement('button'); 115 downloadBtn.className = 'action-btn'; 116 downloadBtn.textContent = 'â¬ï¸'; 117 downloadBtn.title = 'ä¸è½½/æ¥ç'; 118 downloadBtn.onclick = (e) => { 119 e.stopPropagation(); 120 this.downloadFile(file); 121 }; 122 fileActions.appendChild(downloadBtn); 123 } 124 125 const deleteBtn = document.createElement('button'); 126 deleteBtn.className = 'action-btn'; 127 deleteBtn.textContent = 'ðï¸'; 128 deleteBtn.title = 'å é¤'; 129 deleteBtn.onclick = (e) => { 130 e.stopPropagation(); 131 this.deleteFile(file); 132 }; 133 fileActions.appendChild(deleteBtn); 134 135 const renameBtn = document.createElement('button'); 136 renameBtn.className = 'action-btn'; 137 renameBtn.textContent = 'âï¸'; 138 renameBtn.title = 'éå½å'; 139 renameBtn.onclick = (e) => { 140 e.stopPropagation(); 141 this.renameFile(file); 142 }; 143 fileActions.appendChild(renameBtn); 144 145 fileItem.appendChild(fileIcon); 146 fileItem.appendChild(fileName); 147 fileItem.appendChild(fileSize); 148 fileItem.appendChild(fileActions); 149 150 if (file.isDir) { 151 fileItem.onclick = () => this.navigateToDirectory(file); 152 } 153 154 return fileItem; 155 } 156 157 navigateToDirectory(file) { 158 this.currentPath = this.currentPath ? `${this.currentPath}/${file.fileName}` : file.fileName; 159 this.pathInput.value = this.currentPath; 160 this.navigateToDirectoryNotSetPath(file); 161 } 162 163 navigateToDirectoryNotSetPath(file) { 164 165 // 妿æä»¶å¤¹æåæä»¶ï¼ä½¿ç¨åæä»¶å表 166 if (file.urls && file.urls.length > 0) { 167 this.files = file.urls; 168 } else { 169 this.files = []; // 空æä»¶å¤¹ 170 } 171 172 this.updateStorageInfo(); 173 this.renderFiles(); 174 } 175 176 downloadFile(file) { 177 window.open(file.url); 178 } 179 180 deleteFile(file) { 181 if (confirm(`ç¡®å®è¦å é¤ ${file.fileName} åï¼`)) { 182 // è¿éæ·»å å 餿件çé»è¾ 183 this.diskMirror.remove(this.userId, "Binary", this.pathInput.value + '/' + file.fileName, (_) => { 184 this.fileManagerStruct = undefined; 185 this.loadFiles(); 186 }); 187 console.log('å 餿件:', file); 188 } 189 } 190 191 renameFile(file) { 192 const newName = window.prompt('请è¾å ¥æ°æä»¶å:', file.fileName); 193 if (newName && newName !== file.fileName) { 194 // è¿éæ·»å éå½åæä»¶çé»è¾ 195 this.diskMirror.reName(this.userId, "Binary", this.pathInput.value + '/' + file.fileName, this.pathInput.value + '/' + newName, (_) => { 196 this.fileManagerStruct = undefined; 197 this.loadFiles(); 198 }); 199 console.log('éå½åæä»¶:', file.fileName, '->', newName); 200 } 201 } 202 203 /** 204 * 导èªå°è·¯å¾è¾å ¥æ¡ä¸çè·¯å¾ 205 */ 206 toPath() { 207 if (this.currentPath.startsWith('/')) { 208 this.currentPath = this.currentPath.substring(1); 209 } 210 const strings = this.currentPath.split('/'); 211 let tarFile = {}; 212 if (strings.length === 1 && strings[0] === '') { 213 tarFile = this.fileManagerStruct; 214 this.navigateToDirectoryNotSetPath(tarFile); 215 } else if (strings.length >= 1) { 216 for (let string of strings) { 217 for (let fileItem of this.files) { 218 if (fileItem.fileName === string) { 219 this.files = fileItem.urls; 220 tarFile = fileItem; 221 break; 222 } 223 } 224 } 225 this.navigateToDirectoryNotSetPath(tarFile); 226 } 227 } 228 loadFiles() { 229 230 // è¿éæ·»å å è½½æä»¶çé»è¾ 231 // ç¤ºä¾æ°æ® 232 if (this.fileManagerStruct === undefined) { 233 this.diskMirror.getUrls(this.userId, 'Binary', (res) => { 234 this.fileManagerStruct = res; 235 this.setMaxStorage(res['maxSize']) 236 this.handleFileData(this.fileManagerStruct); 237 this.updateStorageInfo(); 238 this.renderFiles(); 239 this.toPath(); 240 }, (err) => console.error(err)); 241 } else { 242 this.handleFileData(this.fileManagerStruct); 243 this.updateStorageInfo(); 244 this.renderFiles(); 245 this.toPath(); 246 } 247 } 248 249 handleFileData(data) { 250 if (!data.res || data.res !== "ok!!!!") { 251 console.error(data); 252 this.showError(); 253 return; 254 } 255 256 this.hideError(); 257 this.files = data.urls; 258 this.usedStorage = data.useSize; 259 this.maxStorage = data.maxSize; 260 261 this.updateStorageInfo(); 262 this.renderFiles(); 263 } 264 265 renderFiles() { 266 this.fileList.innerHTML = ''; 267 268 if (this.files.length === 0) { 269 const emptyMessage = document.createElement('div'); 270 emptyMessage.className = 'empty-message'; 271 emptyMessage.textContent = 'æ¤æä»¶å¤¹ä¸ºç©º';
272 this.fileList.appendChild(emptyMessage); 273 return; 274 } 275 276 this.files.forEach(file => { 277 this.fileList.appendChild(this.createFileElement(file)); 278 }); 279 } 280 281 showError() { 282 this.errorMessage.style.display = 'block'; 283 this.fileList.style.display = 'none'; 284 } 285 286 hideError() { 287 this.errorMessage.style.display = 'none'; 288 this.fileList.style.display = 'grid'; 289 } 290 291 // å¤é¨è°ç¨æ¹æ³ 292 addFile(fileData) { 293 this.files.push(fileData); 294 this.usedStorage += fileData.size; 295 this.updateStorageInfo(); 296 this.renderFiles(); 297 } 298 299 setMaxStorage(size) { 300 this.maxStorage = size; 301 this.updateStorageInfo(); 302 } 303 304 handleFileUpload(event) { 305 let files; 306 if (event instanceof Event) { 307 files = this.fileInput.files; 308 if (!files.length) return; 309 } else { 310 files = [event]; 311 } 312 let backCount = -1; 313 let count = 0; 314 let error = undefined; 315 const iv = setInterval(() => { 316 if (backCount === count) { 317 return; 318 } 319 if (count >= files.length) { 320 openAndUpdate(this.uploadButton, `${files.length} 个æä»¶ä¸ä¼ 宿¯`); 321 clearInterval(iv); 322 // æ¸ ç©ºæä»¶è¾å ¥æ¡ï¼å 许éå¤éæ©ç¸åæä»¶ 323 this.fileInput.value = ''; 324 this.fileManagerStruct = undefined; 325 this.loadFiles(); 326 return; 327 } 328 let file = files[count]; 329 backCount = count; 330 if (error !== undefined) { 331 alert(error); 332 return; 333 } 334 // æ£æ¥æä»¶å¤§å°æ¯å¦è¶ è¿å©ä½ç©ºé´ 335 if (this.usedStorage + file.size > this.maxStorage) { 336 openAndUpdate(this.uploadButton, `åå¨ç©ºé´ä¸è¶³ï¼ã${file.name}ã æªä¸ä¼ æåï¼å½åè¿åº¦ ${count}/${files.length}`); 337 clearInterval(iv); 338 // æ¸ ç©ºæä»¶è¾å ¥æ¡ï¼å 许éå¤éæ©ç¸åæä»¶ 339 this.fileInput.value = ''; 340 } 341 disableAndUpdate(this.uploadButton, `ä¸ä¼ è¿åº¦ï¼${count}/${files.length} 个æä»¶`); 342 const fileName = file.name; 343 try { 344 this.diskMirrorUpload.upload({ 345 userId: this.userId, 346 fileName: this.pathInput.value + '/' + fileName, 347 type: 'Binary' 348 }, file, (fileData) => { 349 fileData.fileName = fileName; 350 fileData.size = fileData['streamSize']; 351 this.addFile(fileData); 352 count += 1; 353 disableAndUpdate(this.uploadButton, `ä¸ä¼ è¿åº¦ï¼${count}/${files.length} 个æä»¶`); 354 }, (err) => { 355 openAndUpdate(this.uploadButton, `ã${file.name}ã ä¸ä¼ é误ï¼å½åè¿åº¦ ${count}/${files.length}`); 356 clearInterval(iv); 357 // æ¸ ç©ºæä»¶è¾å ¥æ¡ï¼å 许éå¤éæ©ç¸åæä»¶ 358 this.fileInput.value = ''; 359 console.error(err); 360 }); 361 } catch (e) { 362 console.error(e); 363 } 364 }, 500); 365 } 366 367 handleMkDir(event) { 368 const fileName = window.prompt('请è¾å ¥æ°æä»¶å¤¹å:', 'myDir'); 369 if (fileName) { 370 this.diskMirror.mkdirs(this.userId, "Binary", this.pathInput.value + '/' + fileName, (_) => { 371 this.fileManagerStruct = undefined; 372 this.loadFiles(); 373 }) 374 } 375 } 376 377 initReadClipboard() { 378 // 设置ç²è´´äºä»¶ 379 readClipboard( 380 [document.getElementById("fileList")], 381 // å¾çæ°æ® 382 (file) => { 383 getFileHash(file, (hash) => { 384 file.name = hash + ".webp"; 385 this.handleFileUpload(file); 386 }, (_) => { 387 file.name = Date.now() + ".webp"; 388 this.handleFileUpload(file); 389 }) 390 }, 391 // ææ¬æ°æ®ä¸éè¦è¿è¡ä»»ä½æä½ 392 (_) => { 393 }, 394 // è§é¢æ°æ® 395 (file) => { 396 // ä¸ä¼ 397 this.handleFileUpload(file); 398 }, 399 // æä»¶æ°æ® 400 (file) => { 401 this.handleFileUpload(file); 402 }) 403 } 404}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.