feat: added file management functionality and API operations

- Improved functionality for file management has been added.
- Added file system management functionality through IPC.
- Added functionality to interact with files including selection, upload, deletion, and batch operations.
- Added new file operations to the custom API, including file select, upload, delete, batch upload, and batch delete functions.
- Implemented feature to select and upload files via API.
This commit is contained in:
kangfenmao
2024-09-10 17:02:07 +08:00
parent 7beea24fc4
commit 2cb461db4e
5 changed files with 134 additions and 22 deletions
+83 -18
View File
@@ -1,4 +1,5 @@
import { app, dialog } from 'electron'
import * as crypto from 'crypto'
import { app, dialog, OpenDialogOptions } from 'electron'
import * as fs from 'fs'
import * as path from 'path'
import { v4 as uuidv4 } from 'uuid'
@@ -6,9 +7,11 @@ import { v4 as uuidv4 } from 'uuid'
interface FileMetadata {
id: string
name: string
fileName: string
path: string
createdAt: Date
size: number
ext: string
createdAt: Date
}
export class File {
@@ -25,41 +28,103 @@ export class File {
}
}
async selectFile(): Promise<FileMetadata | null> {
const result = await dialog.showOpenDialog({
properties: ['openFile']
private async getFileHash(filePath: string): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('md5')
const stream = fs.createReadStream(filePath)
stream.on('data', (data) => hash.update(data))
stream.on('end', () => resolve(hash.digest('hex')))
stream.on('error', reject)
})
}
async findDuplicateFile(filePath: string): Promise<FileMetadata | null> {
const stats = fs.statSync(filePath)
const fileSize = stats.size
const files = await fs.promises.readdir(this.storageDir)
for (const file of files) {
const storedFilePath = path.join(this.storageDir, file)
const storedStats = fs.statSync(storedFilePath)
if (storedStats.size === fileSize) {
const [originalHash, storedHash] = await Promise.all([
this.getFileHash(filePath),
this.getFileHash(storedFilePath)
])
if (originalHash === storedHash) {
const ext = path.extname(file)
return {
id: path.basename(file, ext),
name: path.basename(filePath),
fileName: file,
path: storedFilePath,
createdAt: storedStats.birthtime,
size: storedStats.size,
ext: ext
}
}
}
}
return null
}
async selectFile(options?: OpenDialogOptions): Promise<FileMetadata[] | null> {
const defaultOptions: OpenDialogOptions = {
properties: ['openFile']
}
const dialogOptions = { ...defaultOptions, ...options }
const result = await dialog.showOpenDialog(dialogOptions)
if (result.canceled || result.filePaths.length === 0) {
return null
}
const filePath = result.filePaths[0]
const stats = fs.statSync(filePath)
const fileMetadataPromises = result.filePaths.map(async (filePath) => {
const stats = fs.statSync(filePath)
const ext = path.extname(filePath)
return {
id: uuidv4(),
name: path.basename(filePath),
path: filePath,
createdAt: stats.birthtime,
size: stats.size
}
return {
id: uuidv4(),
name: path.basename(filePath),
fileName: path.basename(filePath),
path: filePath,
createdAt: stats.birthtime,
size: stats.size,
ext: ext
}
})
return Promise.all(fileMetadataPromises)
}
async uploadFile(filePath: string): Promise<FileMetadata> {
const id = uuidv4()
const duplicateFile = await this.findDuplicateFile(filePath)
if (duplicateFile) {
return duplicateFile
}
const uuid = uuidv4()
const name = path.basename(filePath)
const destPath = path.join(this.storageDir, id)
const ext = path.extname(name)
const destPath = path.join(this.storageDir, uuid + ext)
await fs.promises.copyFile(filePath, destPath)
const stats = await fs.promises.stat(destPath)
return {
id,
id: uuid,
name,
fileName: uuid + ext,
path: destPath,
createdAt: stats.birthtime,
size: stats.size
size: stats.size,
ext: ext
}
}
+26 -1
View File
@@ -1,11 +1,14 @@
import { BrowserWindow, ipcMain, session, shell } from 'electron'
import { BrowserWindow, ipcMain, OpenDialogOptions, session, shell } from 'electron'
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
import { File } from './file'
import AppUpdater from './updater'
import { openFile, saveFile } from './utils/file'
import { compress, decompress } from './utils/zip'
import { createMinappWindow } from './window'
const fileManager = new File()
export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
const { autoUpdater } = new AppUpdater(mainWindow)
@@ -31,6 +34,28 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
ipcMain.handle('zip:compress', (_, text: string) => compress(text))
ipcMain.handle('zip:decompress', (_, text: Buffer) => decompress(text))
ipcMain.handle('file:select', async (_, options?: OpenDialogOptions) => {
return await fileManager.selectFile(options)
})
ipcMain.handle('file:upload', async (_, filePath: string) => {
return await fileManager.uploadFile(filePath)
})
ipcMain.handle('file:delete', async (_, fileId: string) => {
await fileManager.deleteFile(fileId)
return { success: true }
})
ipcMain.handle('file:batchUpload', async (_, filePaths: string[]) => {
return await fileManager.batchUploadFiles(filePaths)
})
ipcMain.handle('file:batchDelete', async (_, fileIds: string[]) => {
await fileManager.batchDeleteFiles(fileIds)
return { success: true }
})
ipcMain.handle('minapp', (_, args) => {
createMinappWindow({
url: args.url,