fix: 优化导出功能
优化导出项目框架
This commit is contained in:
parent
3a4bc50d2b
commit
da89e012d4
@ -25,73 +25,150 @@ public class ProjectExportController {
|
||||
// 模板 ZIP 文件路径
|
||||
private static final String FIXED_ZIP_PATH = "E:/opt/gisbi2.0/";
|
||||
|
||||
private static final String FIXED_ZIP_NAME = "a.zip";
|
||||
|
||||
private static final String FIXED_ZIP_FULL_PATH= FIXED_ZIP_PATH + FIXED_ZIP_NAME;
|
||||
|
||||
private static final String FIXED_ZIP_NAME = "backend.zip";
|
||||
|
||||
private static final String FIXED_ZIP_FULL_PATH = FIXED_ZIP_PATH + FIXED_ZIP_NAME;
|
||||
|
||||
// 自定义数据库服务类
|
||||
@Autowired
|
||||
private DatabaseService databaseService;
|
||||
|
||||
/**
|
||||
* 接口:上传项目 ZIP 包,解压 → 导出数据库部分数据 → 插入 SQL 文件 → 重新打包并下载
|
||||
* 接口:导出项目 ZIP 包,插入数据库 SQL 文件后重新打包
|
||||
*/
|
||||
@PostMapping("/export")
|
||||
public void exportProjectWithDbData(
|
||||
@RequestParam("id") String id) throws Exception {
|
||||
|
||||
String fixedZipPath = FIXED_ZIP_FULL_PATH;
|
||||
File fixedZipFile = new File(fixedZipPath);
|
||||
public void exportProjectWithDbData(@RequestParam("id") String id) throws Exception {
|
||||
// 检查原始 ZIP 是否存在
|
||||
File fixedZipFile = new File(FIXED_ZIP_FULL_PATH);
|
||||
if (!fixedZipFile.exists()) {
|
||||
throw new FileNotFoundException("指定的 ZIP 文件不存在: " + fixedZipPath);
|
||||
throw new FileNotFoundException("指定的 ZIP 文件不存在: " + FIXED_ZIP_FULL_PATH);
|
||||
}
|
||||
|
||||
// 创建时间戳和临时目录路径
|
||||
// 创建时间戳和输出 ZIP 名称
|
||||
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
Path tempExtractDir = Paths.get(FIXED_ZIP_PATH, "temp", "project-" + timestamp);
|
||||
|
||||
try {
|
||||
Files.createDirectories(tempExtractDir);
|
||||
|
||||
// 1. 解压模板 ZIP 到临时目录
|
||||
File uploadedZip = new File(tempExtractDir + ".zip");
|
||||
FileCopyUtils.copy(new FileInputStream(fixedZipFile), new FileOutputStream(uploadedZip));
|
||||
ZipUtils.unzip(uploadedZip, tempExtractDir.toFile());
|
||||
|
||||
// 2. 获取解压后的根目录(自动识别第一个子目录)
|
||||
File[] extractedRoots = tempExtractDir.toFile().listFiles(File::isDirectory);
|
||||
if (extractedRoots == null || extractedRoots.length == 0) {
|
||||
throw new IOException("ZIP 解压后未找到项目根目录");
|
||||
}
|
||||
File projectRoot = extractedRoots[0];
|
||||
|
||||
// 3. 获取 db 目录
|
||||
File dbDir = new File(projectRoot, "db");
|
||||
if (!dbDir.exists()) {
|
||||
boolean created = dbDir.mkdirs();
|
||||
if (!created) {
|
||||
throw new IOException("无法创建 db 目录: " + dbDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 查询数据库并生成 SQL 文件,保存到 db 目录中
|
||||
generateSqlFile(id, new File(dbDir, "data.sql").getAbsolutePath());
|
||||
|
||||
// 5. 重新打包整个项目根目录为新的 ZIP 文件
|
||||
String outputZipName = FIXED_ZIP_NAME.replaceFirst("\\..*$", "") +
|
||||
"-with-sql-" + timestamp + ".zip";
|
||||
|
||||
// 定义解压目标路径和最终 ZIP 输出路径
|
||||
// File extractDir = new File(FIXED_ZIP_PATH, "extracted-project");
|
||||
String extractTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
File extractDir = new File(FIXED_ZIP_PATH, "extracted-project-" + extractTime);
|
||||
File finalZip = new File(FIXED_ZIP_PATH + "export/", outputZipName);
|
||||
ZipUtils.zipFolder(projectRoot, finalZip);
|
||||
}catch (Exception e){
|
||||
|
||||
try {
|
||||
// 1. 清空已存在的解压目录(避免旧数据干扰)
|
||||
if (extractDir.exists()) {
|
||||
try {
|
||||
deleteDirectory(extractDir);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("无法清理旧的解压目录: " + extractDir.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
if (!extractDir.mkdirs()) {
|
||||
throw new IOException("无法创建解压目录: " + extractDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
// 2. 直接解压 ZIP 到目标目录
|
||||
ZipUtils.unzip(fixedZipFile, extractDir);
|
||||
|
||||
//TODO 3. 查找 db 目录并生成 SQL 文件
|
||||
createAndGenerateSqlFile(extractDir, id);
|
||||
|
||||
//TODO 4. 给后端配置文件加上必要的配置
|
||||
modifyBackendConfigFiles(extractDir);
|
||||
|
||||
//TODO 5. 生成前端vue文件
|
||||
generateFrontendVueFiles(extractDir);
|
||||
|
||||
// 6. 重新打包整个解压后的目录内容
|
||||
ZipUtils.zipDirectoryContents(extractDir, finalZip);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
// 7. 清理临时文件(可选)
|
||||
deleteDirectory(tempExtractDir.toFile());
|
||||
throw new RuntimeException("导出项目出错");
|
||||
} finally {
|
||||
// 7.删除解压目录
|
||||
deleteDirectory(extractDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 db 目录并生成 data.sql 文件
|
||||
*/
|
||||
private void createAndGenerateSqlFile(File extractDir, String id) throws IOException {
|
||||
File dbDir = new File(extractDir, "backend/db");
|
||||
if (!dbDir.exists() && !dbDir.mkdirs()) {
|
||||
throw new IOException("无法创建 db 目录: " + dbDir.getAbsolutePath());
|
||||
}
|
||||
String sqlFilePath = new File(dbDir, "data.sql").getAbsolutePath();
|
||||
generateSqlFile(id, sqlFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改后端配置文件(支持嵌套 YAML 结构)
|
||||
*/
|
||||
private void modifyBackendConfigFiles(File extractDir) throws IOException {
|
||||
File configPath = new File(extractDir, "backend/src/main/resources/application.yml");
|
||||
if (!configPath.exists()) {
|
||||
throw new FileNotFoundException("配置文件不存在: " + configPath.getAbsolutePath());
|
||||
}
|
||||
|
||||
List<String> lines = Files.readAllLines(configPath.toPath());
|
||||
|
||||
boolean inServerBlock = false;
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
String line = lines.get(i);
|
||||
|
||||
// 判断是否进入 server 块
|
||||
if (line.trim().startsWith("server:")) {
|
||||
inServerBlock = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果在 server 块中,并且遇到下一个顶级 key,则退出 block
|
||||
if (inServerBlock && line.trim().contains(":") && !line.startsWith(" ") && !line.startsWith("\t")) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 修改 port 字段
|
||||
if (inServerBlock && line.trim().startsWith("port:")) {
|
||||
// 获取原缩进
|
||||
int indent = line.indexOf("port:");
|
||||
String newLine = " ".repeat(indent) + "port: 8081";
|
||||
lines.set(i, newLine);
|
||||
break; // 找到并修改后退出
|
||||
}
|
||||
}
|
||||
|
||||
// 写回文件
|
||||
Files.write(configPath.toPath(), lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在前端目录下生成一个 Vue 文件
|
||||
*/
|
||||
private void generateFrontendVueFiles(File extractDir) throws IOException {
|
||||
File vueDir = new File(extractDir, "frontend/src/views/generated");
|
||||
if (!vueDir.exists() && !vueDir.mkdirs()) {
|
||||
throw new IOException("无法创建 Vue 文件目录: " + vueDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
File vueFile = new File(vueDir, "GeneratedPage.vue");
|
||||
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(vueFile))) {
|
||||
writer.write("<template>");
|
||||
writer.newLine();
|
||||
writer.write(" <div>这是自动生成的页面</div>");
|
||||
writer.newLine();
|
||||
writer.write("</template>");
|
||||
writer.newLine();
|
||||
writer.write("<script>");
|
||||
writer.newLine();
|
||||
writer.write("export default { name: 'GeneratedPage' };");
|
||||
writer.newLine();
|
||||
writer.write("</script>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 id 查询数据库,并生成 SQL 文件(CREATE TABLE + INSERT INTO)
|
||||
|
@ -1,34 +1,44 @@
|
||||
package io.gisbi.application.appcode.utils;
|
||||
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Objects;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class ZipUtils {
|
||||
|
||||
public static void unzip(File zipFile, File outputFolder) throws IOException {
|
||||
|
||||
public static void unzip(File zipFile, File destDir) throws IOException {
|
||||
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
File file = new File(outputFolder, entry.getName());
|
||||
File entryDestination = new File(destDir, entry.getName());
|
||||
if (entry.isDirectory()) {
|
||||
Files.createDirectories(file.toPath());
|
||||
entryDestination.mkdirs();
|
||||
} else {
|
||||
Files.createDirectories(file.getParentFile().toPath());
|
||||
FileCopyUtils.copy(zis, new FileOutputStream(file));
|
||||
entryDestination.getParentFile().mkdirs();
|
||||
try (FileOutputStream fos = new FileOutputStream(entryDestination)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int len;
|
||||
while ((len = zis.read(buffer)) > 0) {
|
||||
fos.write(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
zis.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String sanitizePath(String path) {
|
||||
// 替换非法字符
|
||||
return path.replaceAll("[\0<>:\"/\\|?*]", "_");
|
||||
}
|
||||
|
||||
public static void zipFolder(File folder, File zipFile) throws IOException {
|
||||
try (
|
||||
FileOutputStream fos = new FileOutputStream(zipFile);
|
||||
@ -38,6 +48,37 @@ public class ZipUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void zipDirectoryContents(File sourceDir, File outputZip) throws IOException {
|
||||
try (FileOutputStream fos = new FileOutputStream(outputZip);
|
||||
ZipOutputStream zos = new ZipOutputStream(fos)) {
|
||||
|
||||
for (File file : Objects.requireNonNull(sourceDir.listFiles())) {
|
||||
addToZip(file, sourceDir, zos, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addToZip(File file, File baseDir, ZipOutputStream zos, String parent) throws IOException {
|
||||
String entryName = parent + file.getName();
|
||||
if (file.isDirectory()) {
|
||||
for (File child : file.listFiles()) {
|
||||
addToZip(child, baseDir, zos, entryName + "/");
|
||||
}
|
||||
} else {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
ZipEntry entry = new ZipEntry(entryName);
|
||||
zos.putNextEntry(entry);
|
||||
byte[] buffer = new byte[8192];
|
||||
int len;
|
||||
while ((len = fis.read(buffer)) > 0) {
|
||||
zos.write(buffer, 0, len);
|
||||
}
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void zipFile(File fileToZip, String fileName, ZipOutputStream zos) throws IOException {
|
||||
if (fileToZip.isDirectory()) {
|
||||
if (fileName.endsWith("/")) {
|
||||
@ -67,4 +108,17 @@ public class ZipUtils {
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteDirectory(File file) {
|
||||
if (file.isDirectory()) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
deleteDirectory(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,9 +3,9 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>sdk</artifactId>
|
||||
<groupId>io.gisbi</groupId>
|
||||
<version>1.0.0</version>
|
||||
<artifactId>sdk</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user