PageSourceSearch

https://lingyuzhao.top/js/DiskMirrorManager.js

js lingyuzhao.top collected 2026-10-06 13:16:11 UTC 14,307 bytes, 404 lines download raw bytes

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.