mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 15:30:25 +08:00
feature: 导出数据库时采用gzip压缩
This commit is contained in:
@@ -81,7 +81,7 @@
|
||||
<template #more="{ data }">
|
||||
<el-button @click="showInfo(data)" link>详情</el-button>
|
||||
<el-button class="ml5" type="primary" @click="onShowSqlExec(data)" link>SQL执行记录</el-button>
|
||||
<el-button class="ml5" type="primary" @click="onDumpDbs(data)" link>导出</el-button>
|
||||
<el-button v-if="data.type=='mysql'" class="ml5" type="primary" @click="onDumpDbs(data)" link>导出</el-button>
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
@@ -180,14 +180,25 @@
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="601" :title="`${db} 数据库导出`" v-model="exportDialog.visible">
|
||||
<el-form-item label="导出内容: ">
|
||||
<el-radio-group v-model="dumpInfo.type">
|
||||
<el-radio :label="1" size="small">结构</el-radio>
|
||||
<el-radio :label="2" size="small">数据</el-radio>
|
||||
<el-radio :label="3" size="small">结构+数据</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-dialog width="620" :title="`${db} 数据库导出`" v-model="exportDialog.visible">
|
||||
<el-row justify="space-between">
|
||||
<el-col :span="9">
|
||||
<el-form-item label="导出内容: " size="small">
|
||||
<el-checkbox-group v-model="exportDialog.contents" :min=1>
|
||||
<el-checkbox label="结构" />
|
||||
<el-checkbox label="数据" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="9" >
|
||||
<el-form-item label="扩展名: " size="small">
|
||||
<el-radio-group v-model="exportDialog.extName">
|
||||
<el-radio label="sql" />
|
||||
<el-radio label="gz" />
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item>
|
||||
<el-transfer :titles="['全部数据库', '导出数据库']" max-height="300" size="small" v-model="exportDialog.value" :data="exportDialog.data">
|
||||
@@ -333,7 +344,7 @@ const columns = ref([
|
||||
TableColumn.new('name', '名称'),
|
||||
TableColumn.new('database', '数据库').isSlot().setMinWidth(70),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('more', '更多').isSlot().setMinWidth(200).fixedRight(),
|
||||
TableColumn.new('more', '更多').isSlot().setMinWidth(220).fixedRight(),
|
||||
]);
|
||||
|
||||
// 该用户拥有的的操作列按钮权限
|
||||
@@ -422,13 +433,17 @@ const state = reactive({
|
||||
tableNameSearch: '',
|
||||
tableCommentSearch: '',
|
||||
},
|
||||
|
||||
exportDialog: {
|
||||
visible: false,
|
||||
dbId: 0,
|
||||
type: 3,
|
||||
data: [],
|
||||
value: [],
|
||||
contents: [],
|
||||
extName: '',
|
||||
},
|
||||
|
||||
columnDialog: {
|
||||
visible: false,
|
||||
columns: [],
|
||||
@@ -653,6 +668,8 @@ const onDumpDbs = async (row: any) => {
|
||||
state.exportDialog.value = []
|
||||
state.exportDialog.data = data
|
||||
state.exportDialog.dbId = row.id;
|
||||
state.exportDialog.contents = ["结构", "数据"]
|
||||
state.exportDialog.extName = "sql"
|
||||
state.exportDialog.visible = true;
|
||||
};
|
||||
|
||||
@@ -662,9 +679,17 @@ const onDumpDbs = async (row: any) => {
|
||||
const dumpDbs = () => {
|
||||
isTrue(state.exportDialog.value.length > 0, '请添加要导出的数据库');
|
||||
const a = document.createElement('a');
|
||||
let type = 0
|
||||
for (let c of state.exportDialog.contents) {
|
||||
if (c == "结构") {
|
||||
type += 1
|
||||
} else if (c == "数据") {
|
||||
type += 2
|
||||
}
|
||||
}
|
||||
a.setAttribute(
|
||||
'href',
|
||||
`${config.baseApiUrl}/dbs/${state.exportDialog.dbId}/dump?db=${state.exportDialog.value.join(',')}&type=${state.exportDialog.type}&token=${getSession(
|
||||
`${config.baseApiUrl}/dbs/${state.exportDialog.dbId}/dump?db=${state.exportDialog.value.join(',')}&type=${type}&extName=${state.exportDialog.extName}&token=${getSession(
|
||||
'token'
|
||||
)}`
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/internal/db/api/form"
|
||||
@@ -32,6 +33,18 @@ type Db struct {
|
||||
TagApp tagapp.TagTree
|
||||
}
|
||||
|
||||
type gzipResponseWriter struct {
|
||||
writer *gzip.Writer
|
||||
}
|
||||
|
||||
func (g gzipResponseWriter) WriteString(data string) {
|
||||
g.writer.Write([]byte(data))
|
||||
}
|
||||
|
||||
func (g gzipResponseWriter) Close() {
|
||||
g.writer.Close()
|
||||
}
|
||||
|
||||
const DEFAULT_ROW_SIZE = 5000
|
||||
|
||||
// @router /api/dbs [get]
|
||||
@@ -227,6 +240,13 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
||||
dbNamesStr := g.Query("db")
|
||||
dumpType := g.Query("type")
|
||||
tablesStr := g.Query("tables")
|
||||
extName := g.Query("extName")
|
||||
switch extName {
|
||||
case ".gz", ".gzip", "gz", "gzip":
|
||||
extName = ".gz"
|
||||
default:
|
||||
extName = ""
|
||||
}
|
||||
|
||||
// 是否需要导出表结构
|
||||
needStruct := dumpType == "1" || dumpType == "3"
|
||||
@@ -237,9 +257,12 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, db.TagPath), "%s")
|
||||
|
||||
now := time.Now()
|
||||
filename := fmt.Sprintf("%s.%s.sql", db.Name, now.Format("20060102150405"))
|
||||
filename := fmt.Sprintf("%s.%s.sql%s", db.Name, now.Format("20060102150405"), extName)
|
||||
g.Header("Content-Type", "application/octet-stream")
|
||||
g.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
if extName != ".gz" {
|
||||
g.Header("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
var dbNames, tables []string
|
||||
if len(dbNamesStr) > 0 {
|
||||
@@ -248,7 +271,8 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
||||
if len(dbNames) == 1 && len(tablesStr) > 0 {
|
||||
tables = strings.Split(tablesStr, ",")
|
||||
}
|
||||
writer := g.Writer
|
||||
writer := gzipResponseWriter{writer: gzip.NewWriter(g.Writer)}
|
||||
defer writer.Close()
|
||||
for _, dbName := range dbNames {
|
||||
d.dumpDb(writer, db, dbName, tables, needStruct, needData)
|
||||
}
|
||||
@@ -256,7 +280,7 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
||||
rc.ReqParam = fmt.Sprintf("DB[id=%d, tag=%s, name=%s, databases=%s, tables=%s, dumpType=%s]", db.Id, db.TagPath, db.Name, dbNamesStr, tablesStr, dumpType)
|
||||
}
|
||||
|
||||
func (d *Db) dumpDb(writer gin.ResponseWriter, db *entity.Db, dbName string, tables []string, needStruct bool, needData bool) {
|
||||
func (d *Db) dumpDb(writer gzipResponseWriter, db *entity.Db, dbName string, tables []string, needStruct bool, needData bool) {
|
||||
writer.WriteString("-- ----------------------------")
|
||||
writer.WriteString("\n-- 导出平台: mayfly-go")
|
||||
writer.WriteString(fmt.Sprintf("\n-- 导出时间: %s ", time.Now().Format("2006-01-02 15:04:05")))
|
||||
@@ -269,10 +293,8 @@ func (d *Db) dumpDb(writer gin.ResponseWriter, db *entity.Db, dbName string, tab
|
||||
switch dbInst.Info.Type {
|
||||
case entity.DbTypeMysql:
|
||||
writer.WriteString(fmt.Sprintf("use `%s`;\n", dbName))
|
||||
case entity.DbTypePostgres:
|
||||
writer.WriteString(fmt.Sprintf("\\connect `%s`;\n", dbName))
|
||||
default:
|
||||
biz.IsTrue(false, "数据库类型必须为 MySQL 或 PostgreSQL")
|
||||
biz.IsTrue(false, "数据库类型必须为 %s", entity.DbTypeMysql)
|
||||
}
|
||||
dbMeta := dbInst.GetMeta()
|
||||
if len(tables) == 0 {
|
||||
|
||||
Reference in New Issue
Block a user