Files
u-desk/frontend/src/api/sftp-transport.ts
T
lxy e7a2c5148e 新增: 文件操作增强与预览扩展
- 文件剪贴板复制剪切粘贴,重名自动副本
- SFTP下载到本机与外部文件拖入,目录递归
- 传输进度浮动面板,transfer-progress事件链
- Drawio预览,Excel工作线程,冻结看门狗
- cmd与bat自写batch语法高亮,psm1等映射补齐
2026-09-15 22:26:12 +08:00

204 lines
6.2 KiB
TypeScript

/**
* SFTP Transport — 通过 Wails IPC 调用 Go 后端 SFTP 客户端
*/
import type {
FsTransport, FileItem, FileOperationResult, DetectTypeResult,
} from './transport'
import { transformFileList, joinPath, probeAlive } from './transport'
import type { ConnectionProfile } from './connection-manager'
import {
SftpConnect, SftpDisconnect, SftpListDir, SftpReadFile,
SftpWriteFile, SftpGetFileInfo, SftpCreateDir, SftpCreateFile,
SftpDeletePath, SftpRenamePath, SftpDownloadToTemp, SftpDownloadSiteForPreview,
SftpDownloadToFile, SftpUploadFromFile,
SftpGetCommonPaths, SftpWriteBase64File,
} from '@bindings/u-desk/app'
export class SftpTransport implements FsTransport {
private profile: ConnectionProfile
private connID: string | null = null
constructor(profile: ConnectionProfile) {
this.profile = profile
}
async connect(): Promise<string> {
const result = await SftpConnect({
host: this.profile.host,
port: this.profile.port || 22,
username: this.profile.username || 'root',
password: this.profile.password || '',
key_path: this.profile.keyPath || '',
key_passphrase: '',
})
this.connID = result
return result
}
async isAlive(): Promise<boolean> {
return probeAlive(this.connID, id => SftpGetFileInfo(id, '/'))
}
get sessionId(): string | null { return this.connID }
async disconnect(): Promise<void> {
if (this.connID) {
try { await SftpDisconnect(this.connID) } catch (e) {
console.warn('[SFTP] disconnect error:', e)
}
this.connID = null
}
}
private requireConn(): string {
if (!this.connID) throw new Error('SFTP 未连接')
return this.connID
}
// ====== 文件列表与信息 ======
async listDir(path: string): Promise<FileItem[]> {
return transformFileList(await SftpListDir(this.requireConn(), path))
}
async getFileInfo(path: string): Promise<Record<string, any>> {
return SftpGetFileInfo(this.requireConn(), path)
}
// ====== 文件读写 ======
async readFile(path: string): Promise<string> {
return SftpReadFile(this.requireConn(), path)
}
async writeFile(path: string, content: string): Promise<void> {
await SftpWriteFile({
session_id: this.requireConn(),
path,
content,
})
}
async saveBase64File(path: string, content: string): Promise<void> {
if (!content) throw new Error('无效的 base64 内容')
await SftpWriteBase64File(this.requireConn(), path, content)
}
// ====== 文件操作 ======
async createFile(dirPath: string, filename: string): Promise<FileOperationResult> {
return SftpCreateFile(this.requireConn(), joinPath(dirPath, filename))
}
async createDir(parentPath: string, dirname: string): Promise<FileOperationResult> {
return SftpCreateDir(this.requireConn(), joinPath(parentPath, dirname))
}
async deletePath(path: string): Promise<FileOperationResult> {
return SftpDeletePath(this.requireConn(), path)
}
async renamePath(oldPath: string, newPath: string): Promise<FileOperationResult> {
return SftpRenamePath({
session_id: this.requireConn(),
old_path: oldPath,
new_path: newPath,
})
}
// ====== 复制/移动(粘贴仅本地面板支持)======
async copyPath(_src: string, _dst: string): Promise<void> {
throw new Error('SFTP 面板暂不支持粘贴,可使用拖拽上传')
}
async movePath(_src: string, _dst: string): Promise<void> {
throw new Error('SFTP 面板暂不支持粘贴,可使用拖拽上传')
}
// ====== 传输 ======
/** 下载远程文件/目录到用户指定目录(流式无上限,重名自动副本),返回本地落地路径 */
async downloadToFile(remotePath: string, localDstDir: string): Promise<string> {
return SftpDownloadToFile(this.requireConn(), remotePath, localDstDir)
}
/** 上传本地文件/目录到远程目录(流式,目录递归) */
async uploadFromFile(localPath: string, remoteDir: string): Promise<void> {
await SftpUploadFromFile(this.requireConn(), localPath, remoteDir)
}
// ====== ZIP 操作(不支持)======
async listZipContents(_zipPath: string): Promise<FileItem[]> {
throw new Error('ZIP 操作在 SFTP 模式暂未实现')
}
async extractFileFromZip(_zipPath: string, _filePath: string): Promise<string> {
throw new Error('ZIP 操作在 SFTP 模式暂未实现')
}
async extractFileFromZipToTemp(_zipPath: string, _filePath: string): Promise<string> {
throw new Error('ZIP 操作在 SFTP 模式暂未实现')
}
async getZipFileInfo(_zipPath: string, _filePath: string): Promise<FileItem> {
throw new Error('ZIP 操作在 SFTP 模式暂未实现')
}
// ====== 系统操作 ======
async openPath(_path: string): Promise<void> {
throw new Error('SFTP 模式不支持打开本地路径')
}
async getFileServerURL(): Promise<string> {
const { getFileServerBaseURL } = await import('./file-server')
return getFileServerBaseURL()
}
getPreviewToken(): string {
return ''
}
async resolveShortcut(_lnkPath: string): Promise<any> {
return null
}
async detectFileTypeByContent(_path: string): Promise<DetectTypeResult> {
return { extension: '', category: 'unknown', mime_type: '', confidence: 0 }
}
async getCommonPaths(): Promise<Record<string, string>> {
return SftpGetCommonPaths(this.requireConn())
}
// ====== 回收站(无)======
async getRecycleBinEntries(): Promise<any[]> {
return []
}
async restoreFromRecycleBin(_path: string): Promise<void> {}
async deletePermanently(_path: string): Promise<void> {}
async emptyRecycleBin(): Promise<void> {}
// ====== 预览辅助 ======
/** 下载远程文件到本地临时目录用于预览(缓存由 Go 后端管理) */
async downloadForPreview(remotePath: string): Promise<string> {
return SftpDownloadToTemp(this.requireConn(), remotePath)
}
/** 下载 HTML 及其网站资源到临时目录用于预览 */
async downloadSiteForPreview(remotePath: string): Promise<string> {
return SftpDownloadSiteForPreview(this.requireConn(), remotePath)
}
async watchFile(_path: string): Promise<void> { /* SFTP 模式不支持文件监听 */ }
async unwatchFile(): Promise<void> { /* SFTP 模式不支持文件监听 */ }
}