feat: 机器文件优化&sql新增引号包裹

This commit is contained in:
meilin.huang
2023-09-11 22:59:13 +08:00
parent ef8822d671
commit e4e68d02bc
5 changed files with 154 additions and 133 deletions

View File

@@ -266,6 +266,7 @@ const cellClick = (row: any, column: any, cell: any) => {
}; };
const submitUpdateFields = () => { const submitUpdateFields = () => {
const dbInst = DbInst.getInst(state.dbId)
let currentUpdatedFields = state.updatedFields; let currentUpdatedFields = state.updatedFields;
if (currentUpdatedFields.length <= 0) { if (currentUpdatedFields.length <= 0) {
return; return;
@@ -274,12 +275,12 @@ const submitUpdateFields = () => {
let res = ''; let res = '';
let divs: HTMLElement[] = []; let divs: HTMLElement[] = [];
currentUpdatedFields.forEach(a => { currentUpdatedFields.forEach(a => {
let sql = `UPDATE ${state.table} SET `; let sql = `UPDATE ${dbInst.wrapName(state.table)} SET `;
let primaryKey = a.primaryKey; let primaryKey = a.primaryKey;
let primaryKeyType = a.primaryKeyType; let primaryKeyType = a.primaryKeyType;
let primaryKeyName = a.primaryKeyName; let primaryKeyName = a.primaryKeyName;
a.fields.forEach(f => { a.fields.forEach(f => {
sql += ` ${f.fieldName} = ${DbInst.wrapColumnValue(f.fieldType, f.newValue)},` sql += ` ${dbInst.wrapName(f.fieldName)} = ${DbInst.wrapColumnValue(f.fieldType, f.newValue)},`
// 如果修改的字段是主键 // 如果修改的字段是主键
if (f.fieldName === primaryKeyName) { if (f.fieldName === primaryKeyName) {
primaryKey = f.oldValue primaryKey = f.oldValue
@@ -287,11 +288,11 @@ const submitUpdateFields = () => {
divs.push(f.div) divs.push(f.div)
}) })
sql = sql.substring(0, sql.length - 1) sql = sql.substring(0, sql.length - 1)
sql += ` WHERE ${primaryKeyName} = ${DbInst.wrapColumnValue(primaryKeyType, primaryKey)} ;` sql += ` WHERE ${dbInst.wrapName(primaryKeyName)} = ${DbInst.wrapColumnValue(primaryKeyType, primaryKey)} ;`
res += sql; res += sql;
}) })
DbInst.getInst(state.dbId).promptExeSql(db, res, () => { }, () => { dbInst.promptExeSql(db, res, () => { }, () => {
currentUpdatedFields = []; currentUpdatedFields = [];
divs.forEach(a => { divs.forEach(a => {
a.classList.remove('update_field_active'); a.classList.remove('update_field_active');

View File

@@ -283,7 +283,7 @@ const selectData = async () => {
const dbInst = state.ti.getNowDbInst(); const dbInst = state.ti.getNowDbInst();
const { db } = state.ti; const { db } = state.ti;
try { try {
const countRes = await dbInst.runSql(db, DbInst.getDefaultCountSql(state.table, state.condition)); const countRes = await dbInst.runSql(db, dbInst.getDefaultCountSql(state.table, state.condition));
state.count = countRes.res[0].count; state.count = countRes.res[0].count;
let sql = dbInst.getDefaultSelectSql(state.table, state.condition, state.orderBy, state.pageNum, state.pageSize); let sql = dbInst.getDefaultSelectSql(state.table, state.condition, state.orderBy, state.pageNum, state.pageSize);
state.sql = sql; state.sql = sql;
@@ -427,6 +427,7 @@ const closeAddDataDialog = () => {
const addRow = async () => { const addRow = async () => {
dataForm.value.validate(async (valid: boolean) => { dataForm.value.validate(async (valid: boolean) => {
if (valid) { if (valid) {
const dbInst = state.ti.getNowDbInst();
const data = state.addDataDialog.data; const data = state.addDataDialog.data;
// key: 字段名value: 字段名提示 // key: 字段名value: 字段名提示
let obj: any = {}; let obj: any = {};
@@ -435,12 +436,12 @@ const addRow = async () => {
if (!value) { if (!value) {
continue; continue;
} }
obj[`${item.columnName}`] = DbInst.wrapValueByType(value); obj[`${dbInst.wrapName(item.columnName)}`] = DbInst.wrapValueByType(value);
} }
let columnNames = Object.keys(obj).join(','); let columnNames = Object.keys(obj).join(',');
let values = Object.values(obj).join(','); let values = Object.values(obj).join(',');
let sql = `INSERT INTO ${state.table} (${columnNames}) VALUES (${values});`; let sql = `INSERT INTO ${dbInst.wrapName(state.table)} (${columnNames}) VALUES (${values});`;
state.ti.getNowDbInst().promptExeSql(state.ti.db, sql, null, () => { dbInst.promptExeSql(state.ti.db, sql, null, () => {
closeAddDataDialog(); closeAddDataDialog();
onRefresh(); onRefresh();
}); });

View File

@@ -141,9 +141,19 @@ export class DbInst {
}); });
} }
/**
* 获取count sql
* @param table 表名
* @param condition 条件
* @returns count sql
*/
getDefaultCountSql = (table: string, condition?: string) => {
return `SELECT COUNT(*) count FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} limit 1`;
};
// 获取指定表的默认查询sql // 获取指定表的默认查询sql
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number = DbInst.DefaultLimit) { getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number = DbInst.DefaultLimit) {
const baseSql = `SELECT * FROM ${table} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''}`; const baseSql = `SELECT * FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''}`;
if (this.type == 'mysql') { if (this.type == 'mysql') {
return `${baseSql} LIMIT ${(pageNum - 1) * limit}, ${limit};`; return `${baseSql} LIMIT ${(pageNum - 1) * limit}, ${limit};`;
} }
@@ -170,10 +180,10 @@ export class DbInst {
let values = []; let values = [];
for (let column of columns) { for (let column of columns) {
const colName = column.columnName; const colName = column.columnName;
colNames.push(colName); colNames.push(this.wrapName(colName));
values.push(DbInst.wrapValueByType(data[colName])); values.push(DbInst.wrapValueByType(data[colName]));
} }
sqls.push(`INSERT INTO ${table} (${colNames.join(', ')}) VALUES(${values.join(', ')})`); sqls.push(`INSERT INTO ${this.wrapName(table)} (${colNames.join(', ')}) VALUES(${values.join(', ')})`);
} }
return sqls.join(';\n') + ';'; return sqls.join(';\n') + ';';
} }
@@ -187,7 +197,7 @@ export class DbInst {
const primaryKey = this.getDb(db).getColumn(table); const primaryKey = this.getDb(db).getColumn(table);
const primaryKeyColumnName = primaryKey.columnName; const primaryKeyColumnName = primaryKey.columnName;
const ids = datas.map((d: any) => `${DbInst.wrapColumnValue(primaryKey.columnType, d[primaryKeyColumnName])}`).join(','); const ids = datas.map((d: any) => `${DbInst.wrapColumnValue(primaryKey.columnType, d[primaryKeyColumnName])}`).join(',');
return `DELETE FROM ${table} WHERE ${primaryKeyColumnName} IN (${ids})`; return `DELETE FROM ${this.wrapName(table)} WHERE ${this.wrapName(primaryKeyColumnName)} IN (${ids})`;
} }
/* /*
@@ -203,6 +213,22 @@ export class DbInst {
}); });
}; };
/**
* 包裹数据库表名、字段名等,避免使用关键字为字段名或表名时报错
* @param table
* @param condition
* @returns
*/
wrapName = (name: string) => {
if (this.type == 'mysql') {
return `\`${name}\``;
}
if (this.type == 'postgres') {
return `"${name}"`;
}
return name;
};
/** /**
* 获取或新建dbInst如果缓存中不存在则新建否则直接返回 * 获取或新建dbInst如果缓存中不存在则新建否则直接返回
* @param inst 数据库实例,后端返回的列表接口中的信息 * @param inst 数据库实例,后端返回的列表接口中的信息
@@ -252,16 +278,6 @@ export class DbInst {
dbInstCache.clear(); dbInstCache.clear();
} }
/**
* 获取count sql
* @param table 表名
* @param condition 条件
* @returns count sql
*/
static getDefaultCountSql = (table: string, condition?: string) => {
return `SELECT COUNT(*) count FROM ${table} ${condition ? 'WHERE ' + condition : ''}`;
};
/** /**
* 根据返回值包装值,若值为字符串类型则添加'' * 根据返回值包装值,若值为字符串类型则添加''
* @param val 值 * @param val 值

View File

@@ -8,6 +8,7 @@ import (
"mayfly-go/internal/machine/api/vo" "mayfly-go/internal/machine/api/vo"
"mayfly-go/internal/machine/application" "mayfly-go/internal/machine/application"
"mayfly-go/internal/machine/domain/entity" "mayfly-go/internal/machine/domain/entity"
"mayfly-go/internal/machine/infrastructure/machine"
msgapp "mayfly-go/internal/msg/application" msgapp "mayfly-go/internal/msg/application"
"mayfly-go/pkg/biz" "mayfly-go/pkg/biz"
"mayfly-go/pkg/ginx" "mayfly-go/pkg/ginx"
@@ -67,18 +68,19 @@ func (m *MachineFile) CreateFile(rc *req.Ctx) {
form := ginx.BindJsonAndValid(g, new(form.MachineCreateFileForm)) form := ginx.BindJsonAndValid(g, new(form.MachineCreateFileForm))
path := form.Path path := form.Path
mi := m.MachineFileApp.GetMachine(fid) attrs := jsonx.Kvs("path", path)
var mi *machine.Info
attrs := jsonx.Kvs("machine", mi, "path", path) var err error
rc.ReqParam = attrs
if form.Type == dir { if form.Type == dir {
attrs["type"] = "目录" attrs["type"] = "目录"
m.MachineFileApp.MkDir(fid, form.Path) mi, err = m.MachineFileApp.MkDir(fid, form.Path)
} else { } else {
attrs["type"] = "文件" attrs["type"] = "文件"
m.MachineFileApp.CreateFile(fid, form.Path) mi, err = m.MachineFileApp.CreateFile(fid, form.Path)
} }
attrs["machine"] = mi
rc.ReqParam = attrs
biz.ErrIsNilAppendErr(err, "创建目录失败: %s")
} }
func (m *MachineFile) ReadFileContent(rc *req.Ctx) { func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
@@ -87,7 +89,9 @@ func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
readPath := g.Query("path") readPath := g.Query("path")
readType := g.Query("type") readType := g.Query("type")
sftpFile := m.MachineFileApp.ReadFile(fid, readPath) sftpFile, mi, err := m.MachineFileApp.ReadFile(fid, readPath)
rc.ReqParam = jsonx.Kvs("machine", mi, "path", readPath)
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
defer sftpFile.Close() defer sftpFile.Close()
fileInfo, _ := sftpFile.Stat() fileInfo, _ := sftpFile.Stat()
@@ -96,9 +100,6 @@ func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
if readType != "1" { if readType != "1" {
biz.IsTrue(filesize < max_read_size, "文件超过1m请使用下载查看") biz.IsTrue(filesize < max_read_size, "文件超过1m请使用下载查看")
} }
mi := m.MachineFileApp.GetMachine(fid)
rc.ReqParam = jsonx.Kvs("machine", mi, "path", readPath, "filesize", filesize)
// 如果读取类型为下载,则下载文件,否则获取文件内容 // 如果读取类型为下载,则下载文件,否则获取文件内容
if readType == "1" { if readType == "1" {
// 截取文件名,如/usr/local/test.java -》 test.java // 截取文件名,如/usr/local/test.java -》 test.java
@@ -161,10 +162,9 @@ func (m *MachineFile) WriteFileContent(rc *req.Ctx) {
ginx.BindJsonAndValid(g, form) ginx.BindJsonAndValid(g, form)
path := form.Path path := form.Path
mi := m.MachineFileApp.GetMachine(fid) mi, err := m.MachineFileApp.WriteFileContent(fid, path, []byte(form.Content))
rc.ReqParam = jsonx.Kvs("machine", mi, "path", path) rc.ReqParam = jsonx.Kvs("machine", mi, "path", path)
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
m.MachineFileApp.WriteFileContent(fid, path, []byte(form.Content))
} }
const MaxUploadFileSize int64 = 1024 * 1024 * 1024 const MaxUploadFileSize int64 = 1024 * 1024 * 1024
@@ -181,9 +181,6 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) {
file, _ := fileheader.Open() file, _ := fileheader.Open()
defer file.Close() defer file.Close()
mi := m.MachineFileApp.GetMachine(fid)
rc.ReqParam = jsonx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", path, fileheader.Filename))
la := rc.LoginAccount la := rc.LoginAccount
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
@@ -195,7 +192,9 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) {
} }
}() }()
m.MachineFileApp.UploadFile(fid, path, fileheader.Filename, file) mi, err := m.MachineFileApp.UploadFile(fid, path, fileheader.Filename, file)
rc.ReqParam = jsonx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", path, fileheader.Filename))
biz.ErrIsNilAppendErr(err, "创建文件失败: %s")
// 保存消息并发送文件上传成功通知 // 保存消息并发送文件上传成功通知
m.MsgApp.CreateAndSend(la, ws.SuccessMsg("文件上传成功", fmt.Sprintf("[%s]文件已成功上传至 %s[%s:%s]", fileheader.Filename, mi.Name, mi.Ip, path))) m.MsgApp.CreateAndSend(la, ws.SuccessMsg("文件上传成功", fmt.Sprintf("[%s]文件已成功上传至 %s[%s:%s]", fileheader.Filename, mi.Name, mi.Ip, path)))
} }
@@ -224,7 +223,9 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
paths := mf.Value["paths"] paths := mf.Value["paths"]
folderName := filepath.Dir(paths[0]) folderName := filepath.Dir(paths[0])
mi := m.MachineFileApp.GetMachine(fid) mcli := m.MachineFileApp.GetMachineCli(fid, basePath+"/"+folderName)
mi := mcli.GetMachine()
sftpCli := mcli.GetSftpCli()
rc.ReqParam = jsonx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", basePath, folderName)) rc.ReqParam = jsonx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", basePath, folderName))
folderFiles := make([]FolderFile, len(paths)) folderFiles := make([]FolderFile, len(paths))
@@ -234,7 +235,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
dir := filepath.Dir(path) dir := filepath.Dir(path)
// 目录已建,则无需重复建 // 目录已建,则无需重复建
if !mkdirs[dir] { if !mkdirs[dir] {
m.MachineFileApp.MkDir(fid, basePath+"/"+dir) biz.ErrIsNilAppendErr(sftpCli.MkdirAll(basePath+"/"+dir), "创建目录失败: %s")
mkdirs[dir] = true mkdirs[dir] = true
} }
folderFiles[i] = FolderFile{ folderFiles[i] = FolderFile{
@@ -244,7 +245,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
} }
// 分组处理 // 分组处理
groupNum := 10 groupNum := 30
chunks := collx.ArraySplit(folderFiles, groupNum) chunks := collx.ArraySplit(folderFiles, groupNum)
var wg sync.WaitGroup var wg sync.WaitGroup
@@ -271,15 +272,19 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
dir := file.Dir dir := file.Dir
file, _ := fileHeader.Open() file, _ := fileHeader.Open()
defer file.Close() defer file.Close()
logx.Debugf("上传文件夹: dir=%s -> filename=%s", dir, fileHeader.Filename) logx.Debugf("上传文件夹: dir=%s -> filename=%s", dir, fileHeader.Filename)
m.MachineFileApp.UploadFile(fid, basePath+"/"+dir, fileHeader.Filename, file)
createfile, err := sftpCli.Create(fmt.Sprintf("%s/%s/%s", basePath, dir, fileHeader.Filename))
biz.ErrIsNilAppendErr(err, "创建文件失败: %s")
defer createfile.Close()
io.Copy(createfile, file)
} }
}(chunk, &wg) }(chunk, &wg)
} }
// 等待所有协程执行完成 // 等待所有协程执行完成
wg.Wait() wg.Wait()
// 保存消息并发送文件上传成功通知 // 保存消息并发送文件上传成功通知
m.MsgApp.CreateAndSend(rc.LoginAccount, ws.SuccessMsg("文件上传成功", fmt.Sprintf("[%s]文件夹已成功上传至 %s[%s:%s]", folderName, mi.Name, mi.Ip, basePath))) m.MsgApp.CreateAndSend(rc.LoginAccount, ws.SuccessMsg("文件上传成功", fmt.Sprintf("[%s]文件夹已成功上传至 %s[%s:%s]", folderName, mi.Name, mi.Ip, basePath)))
} }
@@ -291,10 +296,9 @@ func (m *MachineFile) RemoveFile(rc *req.Ctx) {
rmForm := new(form.MachineFileOpForm) rmForm := new(form.MachineFileOpForm)
ginx.BindJsonAndValid(g, rmForm) ginx.BindJsonAndValid(g, rmForm)
mi := m.MachineFileApp.GetMachine(fid) mi, err := m.MachineFileApp.RemoveFile(fid, rmForm.Path...)
rc.ReqParam = jsonx.Kvs("machine", mi, "path", rmForm.Path) rc.ReqParam = jsonx.Kvs("machine", mi, "path", rmForm.Path)
biz.ErrIsNilAppendErr(err, "删除文件失败: %s")
m.MachineFileApp.RemoveFile(fid, rmForm.Path...)
} }
func (m *MachineFile) CopyFile(rc *req.Ctx) { func (m *MachineFile) CopyFile(rc *req.Ctx) {
@@ -303,7 +307,8 @@ func (m *MachineFile) CopyFile(rc *req.Ctx) {
cpForm := new(form.MachineFileOpForm) cpForm := new(form.MachineFileOpForm)
ginx.BindJsonAndValid(g, cpForm) ginx.BindJsonAndValid(g, cpForm)
mi := m.MachineFileApp.Copy(fid, cpForm.ToPath, cpForm.Path...) mi, err := m.MachineFileApp.Copy(fid, cpForm.ToPath, cpForm.Path...)
biz.ErrIsNilAppendErr(err, "文件拷贝失败: %s")
rc.ReqParam = jsonx.Kvs("machine", mi, "cp", cpForm) rc.ReqParam = jsonx.Kvs("machine", mi, "cp", cpForm)
} }
@@ -313,8 +318,9 @@ func (m *MachineFile) MvFile(rc *req.Ctx) {
cpForm := new(form.MachineFileOpForm) cpForm := new(form.MachineFileOpForm)
ginx.BindJsonAndValid(g, cpForm) ginx.BindJsonAndValid(g, cpForm)
mi := m.MachineFileApp.Mv(fid, cpForm.ToPath, cpForm.Path...) mi, err := m.MachineFileApp.Mv(fid, cpForm.ToPath, cpForm.Path...)
rc.ReqParam = jsonx.Kvs("machine", mi, "mv", cpForm) rc.ReqParam = jsonx.Kvs("machine", mi, "mv", cpForm)
biz.ErrIsNilAppendErr(err, "文件移动失败: %s")
} }
func (m *MachineFile) Rename(rc *req.Ctx) { func (m *MachineFile) Rename(rc *req.Ctx) {
@@ -323,10 +329,9 @@ func (m *MachineFile) Rename(rc *req.Ctx) {
rename := new(form.MachineFileRename) rename := new(form.MachineFileRename)
ginx.BindJsonAndValid(g, rename) ginx.BindJsonAndValid(g, rename)
biz.ErrIsNilAppendErr(m.MachineFileApp.Rename(fid, rename.Oldname, rename.Newname), "文件重命名失败: %s") mi, err := m.MachineFileApp.Rename(fid, rename.Oldname, rename.Newname)
mi := m.MachineFileApp.GetMachine(fid)
rc.ReqParam = jsonx.Kvs("machine", mi, "rename", rename) rc.ReqParam = jsonx.Kvs("machine", mi, "rename", rename)
biz.ErrIsNilAppendErr(err, "文件重命名失败: %s")
} }
func getFileType(fm fs.FileMode) string { func getFileType(fm fs.FileMode) string {

View File

@@ -1,6 +1,7 @@
package application package application
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
@@ -31,15 +32,18 @@ type MachineFile interface {
Delete(id uint64) Delete(id uint64)
// 获取文件关联的机器信息,主要用于记录日志使用 // 获取文件关联的机器信息,主要用于记录日志使用
GetMachine(fileId uint64) *machine.Info // GetMachine(fileId uint64) *machine.Info
// 检查文件路径并返回机器id
GetMachineCli(fileId uint64, path ...string) *machine.Cli
/** sftp 相关操作 **/ /** sftp 相关操作 **/
// 创建目录 // 创建目录
MkDir(fid uint64, path string) MkDir(fid uint64, path string) (*machine.Info, error)
// 创建文件 // 创建文件
CreateFile(fid uint64, path string) CreateFile(fid uint64, path string) (*machine.Info, error)
// 读取目录 // 读取目录
ReadDir(fid uint64, path string) []fs.FileInfo ReadDir(fid uint64, path string) []fs.FileInfo
@@ -51,22 +55,22 @@ type MachineFile interface {
FileStat(fid uint64, path string) string FileStat(fid uint64, path string) string
// 读取文件内容 // 读取文件内容
ReadFile(fileId uint64, path string) *sftp.File ReadFile(fileId uint64, path string) (*sftp.File, *machine.Info, error)
// 写文件 // 写文件
WriteFileContent(fileId uint64, path string, content []byte) WriteFileContent(fileId uint64, path string, content []byte) (*machine.Info, error)
// 文件上传 // 文件上传
UploadFile(fileId uint64, path, filename string, reader io.Reader) UploadFile(fileId uint64, path, filename string, reader io.Reader) (*machine.Info, error)
// 移除文件 // 移除文件
RemoveFile(fileId uint64, path ...string) RemoveFile(fileId uint64, path ...string) (*machine.Info, error)
Copy(fileId uint64, toPath string, paths ...string) *machine.Info Copy(fileId uint64, toPath string, paths ...string) (*machine.Info, error)
Mv(fileId uint64, toPath string, paths ...string) *machine.Info Mv(fileId uint64, toPath string, paths ...string) (*machine.Info, error)
Rename(fileId uint64, oldname string, newname string) error Rename(fileId uint64, oldname string, newname string) (*machine.Info, error)
} }
func newMachineFileApp(machineFileRepo repository.MachineFile, machineRepo repository.Machine) MachineFile { func newMachineFileApp(machineFileRepo repository.MachineFile, machineRepo repository.Machine) MachineFile {
@@ -111,20 +115,19 @@ func (m *machineFileAppImpl) Delete(id uint64) {
} }
func (m *machineFileAppImpl) ReadDir(fid uint64, path string) []fs.FileInfo { func (m *machineFileAppImpl) ReadDir(fid uint64, path string) []fs.FileInfo {
machineId := m.checkAndReturnMid(fid, path) mcli := m.GetMachineCli(fid, path)
if !strings.HasSuffix(path, "/") { if !strings.HasSuffix(path, "/") {
path = path + "/" path = path + "/"
} }
sftpCli := m.getSftpCli(machineId) fis, err := mcli.GetSftpCli().ReadDir(path)
fis, err := sftpCli.ReadDir(path)
biz.ErrIsNilAppendErr(err, "读取目录失败: %s") biz.ErrIsNilAppendErr(err, "读取目录失败: %s")
return fis return fis
} }
func (m *machineFileAppImpl) GetDirSize(fid uint64, path string) string { func (m *machineFileAppImpl) GetDirSize(fid uint64, path string) string {
machineId := m.checkAndReturnMid(fid, path) mcli := m.GetMachineCli(fid, path)
res, err := GetMachineApp().GetCli(machineId).Run(fmt.Sprintf("du -sh %s", path)) res, err := mcli.Run(fmt.Sprintf("du -sh %s", path))
if err != nil { if err != nil {
// 若存在目录为空,则可能会返回如下内容。最后一行即为真正目录内容所占磁盘空间大小 // 若存在目录为空,则可能会返回如下内容。最后一行即为真正目录内容所占磁盘空间大小
//du: cannot access /proc/19087/fd/3: No such file or directory\n //du: cannot access /proc/19087/fd/3: No such file or directory\n
@@ -145,121 +148,116 @@ func (m *machineFileAppImpl) GetDirSize(fid uint64, path string) string {
} }
func (m *machineFileAppImpl) FileStat(fid uint64, path string) string { func (m *machineFileAppImpl) FileStat(fid uint64, path string) string {
machineId := m.checkAndReturnMid(fid, path) mcli := m.GetMachineCli(fid, path)
res, err := GetMachineApp().GetCli(machineId).Run(fmt.Sprintf("stat -L %s", path)) res, err := mcli.Run(fmt.Sprintf("stat -L %s", path))
biz.ErrIsNil(err, res) biz.ErrIsNil(err, res)
return res return res
} }
func (m *machineFileAppImpl) MkDir(fid uint64, path string) { func (m *machineFileAppImpl) MkDir(fid uint64, path string) (*machine.Info, error) {
machineId := m.checkAndReturnMid(fid, path) mcli := m.GetMachineCli(fid, path)
if !strings.HasSuffix(path, "/") { if !strings.HasSuffix(path, "/") {
path = path + "/" path = path + "/"
} }
sftpCli := m.getSftpCli(machineId) err := mcli.GetSftpCli().MkdirAll(path)
err := sftpCli.MkdirAll(path) return mcli.GetMachine(), err
biz.ErrIsNilAppendErr(err, "创建目录失败: %s")
} }
func (m *machineFileAppImpl) CreateFile(fid uint64, path string) { func (m *machineFileAppImpl) CreateFile(fid uint64, path string) (*machine.Info, error) {
machineId := m.checkAndReturnMid(fid, path) mcli := m.GetMachineCli(fid, path)
sftpCli := m.getSftpCli(machineId) file, err := mcli.GetSftpCli().Create(path)
file, err := sftpCli.Create(path)
biz.ErrIsNilAppendErr(err, "创建文件失败: %s") biz.ErrIsNilAppendErr(err, "创建文件失败: %s")
defer file.Close() defer file.Close()
return mcli.GetMachine(), err
} }
func (m *machineFileAppImpl) ReadFile(fileId uint64, path string) *sftp.File { func (m *machineFileAppImpl) ReadFile(fileId uint64, path string) (*sftp.File, *machine.Info, error) {
machineId := m.checkAndReturnMid(fileId, path) mcli := m.GetMachineCli(fileId, path)
sftpCli := m.getSftpCli(machineId)
// 读取文件内容 // 读取文件内容
fc, err := sftpCli.Open(path) fc, err := mcli.GetSftpCli().Open(path)
biz.ErrIsNilAppendErr(err, "打开文件失败: %s") return fc, mcli.GetMachine(), err
return fc
} }
// 写文件内容 // 写文件内容
func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, content []byte) { func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, content []byte) (*machine.Info, error) {
machineId := m.checkAndReturnMid(fileId, path) mcli := m.GetMachineCli(fileId, path)
mi := mcli.GetMachine()
sftpCli := m.getSftpCli(machineId) f, err := mcli.GetSftpCli().OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE|os.O_RDWR)
f, err := sftpCli.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE|os.O_RDWR) if err != nil {
biz.ErrIsNilAppendErr(err, "打开文件失败: %s") return mi, err
}
defer f.Close() defer f.Close()
fi, _ := f.Stat()
biz.IsTrue(!fi.IsDir(), "该路径不是文件")
f.Write(content) f.Write(content)
return mi, err
} }
// 上传文件 // 上传文件
func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, reader io.Reader) { func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, reader io.Reader) (*machine.Info, error) {
machineId := m.checkAndReturnMid(fileId, path) mcli := m.GetMachineCli(fileId, path)
mi := mcli.GetMachine()
if !strings.HasSuffix(path, "/") { if !strings.HasSuffix(path, "/") {
path = path + "/" path = path + "/"
} }
sftpCli := m.getSftpCli(machineId) createfile, err := mcli.GetSftpCli().Create(path + filename)
createfile, err := sftpCli.Create(path + filename) if err != nil {
biz.ErrIsNilAppendErr(err, "创建文件失败: %s") return mi, err
}
defer createfile.Close() defer createfile.Close()
io.Copy(createfile, reader) io.Copy(createfile, reader)
return mi, err
} }
// 删除文件 // 删除文件
func (m *machineFileAppImpl) RemoveFile(fileId uint64, path ...string) { func (m *machineFileAppImpl) RemoveFile(fileId uint64, path ...string) (*machine.Info, error) {
machineId := m.checkAndReturnMid(fileId, path...) mcli := m.GetMachineCli(fileId, path...)
minfo := mcli.GetMachine()
// 优先使用命令删除速度快sftp需要递归遍历删除子文件等 // 优先使用命令删除速度快sftp需要递归遍历删除子文件等
mcli := GetMachineApp().GetCli(machineId)
res, err := mcli.Run(fmt.Sprintf("rm -rf %s", strings.Join(path, " "))) res, err := mcli.Run(fmt.Sprintf("rm -rf %s", strings.Join(path, " ")))
if err == nil { if err == nil {
return return minfo, nil
} }
logx.Errorf("使用命令rm删除文件失败: %s", res) logx.Errorf("使用命令rm删除文件失败: %s", res)
sftpCli := m.getSftpCli(machineId) sftpCli := mcli.GetSftpCli()
for _, p := range path { for _, p := range path {
err := sftpCli.RemoveAll(p) err = sftpCli.RemoveAll(p)
biz.ErrIsNilAppendErr(err, "删除文件失败: %s") if err != nil {
break
} }
} }
return minfo, err
}
func (m *machineFileAppImpl) Copy(fileId uint64, toPath string, paths ...string) *machine.Info { func (m *machineFileAppImpl) Copy(fileId uint64, toPath string, paths ...string) (*machine.Info, error) {
mid := m.checkAndReturnMid(fileId, paths...) mcli := m.GetMachineCli(fileId, paths...)
mcli := GetMachineApp().GetCli(mid) mi := mcli.GetMachine()
res, err := mcli.Run(fmt.Sprintf("cp -r %s %s", strings.Join(paths, " "), toPath)) res, err := mcli.Run(fmt.Sprintf("cp -r %s %s", strings.Join(paths, " "), toPath))
biz.ErrIsNil(err, "文件拷贝失败: %s", res) if err != nil {
return mcli.GetMachine() return mi, errors.New(res)
}
return mi, err
} }
func (m *machineFileAppImpl) Mv(fileId uint64, toPath string, paths ...string) *machine.Info { func (m *machineFileAppImpl) Mv(fileId uint64, toPath string, paths ...string) (*machine.Info, error) {
mid := m.checkAndReturnMid(fileId, paths...) mcli := m.GetMachineCli(fileId, paths...)
mcli := GetMachineApp().GetCli(mid) mi := mcli.GetMachine()
res, err := mcli.Run(fmt.Sprintf("mv %s %s", strings.Join(paths, " "), toPath)) res, err := mcli.Run(fmt.Sprintf("mv %s %s", strings.Join(paths, " "), toPath))
biz.ErrIsNil(err, "文件移动失败: %s", res) if err != nil {
return mcli.GetMachine() return mi, errors.New(res)
}
return mi, err
} }
func (m *machineFileAppImpl) Rename(fileId uint64, oldname string, newname string) error { func (m *machineFileAppImpl) Rename(fileId uint64, oldname string, newname string) (*machine.Info, error) {
mid := m.checkAndReturnMid(fileId, newname) mcli := m.GetMachineCli(fileId, newname)
sftpCli := m.getSftpCli(mid) return mcli.GetMachine(), mcli.GetSftpCli().Rename(oldname, newname)
return sftpCli.Rename(oldname, newname)
} }
// 获取sftp client // 获取文件机器cli
func (m *machineFileAppImpl) getSftpCli(machineId uint64) *sftp.Client { func (m *machineFileAppImpl) GetMachineCli(fid uint64, inputPath ...string) *machine.Cli {
return GetMachineApp().GetCli(machineId).GetSftpCli()
}
func (m *machineFileAppImpl) GetMachine(fileId uint64) *machine.Info {
return GetMachineApp().GetCli(m.GetById(fileId).MachineId).GetMachine()
}
// 校验并返回实际可访问的文件path
func (m *machineFileAppImpl) checkAndReturnMid(fid uint64, inputPath ...string) uint64 {
biz.IsTrue(fid != 0, "文件id不能为空") biz.IsTrue(fid != 0, "文件id不能为空")
mf := m.GetById(fid) mf := m.GetById(fid)
biz.NotNil(mf, "文件不存在") biz.NotNil(mf, "文件不存在")
@@ -267,5 +265,5 @@ func (m *machineFileAppImpl) checkAndReturnMid(fid uint64, inputPath ...string)
// 接口传入的地址需为配置路径的子路径 // 接口传入的地址需为配置路径的子路径
biz.IsTrue(strings.HasPrefix(path, mf.Path), "无权访问该目录或文件: %s", path) biz.IsTrue(strings.HasPrefix(path, mf.Path), "无权访问该目录或文件: %s", path)
} }
return mf.MachineId return GetMachineApp().GetCli(mf.MachineId)
} }