导出
后端:
@PostMapping("/exportExcelData") public void exportExcelData(HttpServletRequest request, HttpServletResponse response, @RequestBody ResData resData) throws IOException { List<Long> menuIds = resData.getMenuIds(); List<Conversion> conversions = new ArrayList<>(); List<String> ktrFilePaths = new ArrayList<>(); for (Long menuId : menuIds) { Conversion conversion = conversionMapper.selectById(menuId); if (conversion != null) { conversions.add(conversion); String ktrFilePath = fileService.getKtrFilePathById(menuId); if (ktrFilePath != null && !ktrFilePaths.contains(ktrFilePath)) { ktrFilePaths.add(ktrFilePath); } } } // 创建Excel工作簿 HSSFWorkbook workbook = new HSSFWorkbook(); // 创建一个工作表 Sheet sheet = workbook.createSheet("Conversions"); // 创建单元格样式,并设置为文本格式 CellStyle textStyle = workbook.createCellStyle(); DataFormat format = workbook.createDataFormat(); textStyle.setDataFormat(format.getFormat("@")); // "@" 表示文本格式 // 创建标题行 Row titleRow = sheet.createRow(0); // 创建单元格样式,并设置为文本格式 titleRow.createCell(0).setCellValue("主键"); titleRow.createCell(1).setCellValue("分组id"); titleRow.createCell(2).setCellValue("名称"); titleRow.createCell(3).setCellValue("备注"); titleRow.createCell(4).setCellValue("创建人"); titleRow.createCell(5).setCellValue("关联状态"); titleRow.createCell(6).setCellValue("XMl"); titleRow.createCell(7).setCellValue("创建时间"); // 应用文本格式到标题行的特定单元格 titleRow.getCell(0).setCellStyle(textStyle); titleRow.getCell(1).setCellStyle(textStyle); // 填充数据 int rowNum = 1; for (Conversion conversion : conversions) { Row row = sheet.createRow(rowNum++); Cell cell = row.createCell(0); cell.setCellValue(String.valueOf(conversion.getId())); cell.setCellStyle(textStyle); cell = row.createCell(1); cell.setCellValue(String.valueOf(conversion.getGroupId())); cell.setCellStyle(textStyle); // 应用文本格式 row.createCell(2).setCellValue(conversion.getName()); row.createCell(3).setCellValue(conversion.getRemark()); row.createCell(4).setCellValue(conversion.getCreateUserName()); row.createCell(5).setCellValue(conversion.getState()); row.createCell(6).setCellValue(conversion.getEditXml()); row.createCell(7).setCellValue(String.valueOf(conversion.getCreateTime())); } // 设置响应头 response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("conversions.xls", "UTF-8")); // 设置响应头 response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("conversions.zip", "UTF-8")); ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream()); // 将Excel文件添加到压缩包 ZipEntry excelEntry = new ZipEntry("conversions.xls"); zipOut.putNextEntry(excelEntry); workbook.write(zipOut); zipOut.closeEntry(); workbook.close(); // 添加.ktr文件到ktr目录 for (String filePath : ktrFilePaths) { File ktrFile = new File(filePath); if (ktrFile.exists()) { FileInputStream fis = new FileInputStream(ktrFile); // 创建ZipEntry时,需要包含ktr目录 ZipEntry ktrEntry = new ZipEntry("ktr/" + ktrFile.getName()); zipOut.putNextEntry(ktrEntry); byte[] bytes = new byte[1024]; int length; while ((length = fis.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } fis.close(); zipOut.closeEntry(); } } // 完成压缩包 zipOut.finish(); zipOut.close(); }
导出后的文件组成:
编辑
编辑
编辑
excel文件:
编辑
前端:
<template> <div class="app-container" style="width:100%;"> <el-form label-width="80px" label-position="left"> <el-form-item label="模型树"> <el-tree ref="tree" :data="treeData" show-checkbox :default-expand-all="false" node-key="id" highlight-current :props="defaultProps" /> </el-form-item> </el-form> <div style="text-align: center;width:100%;"> <el-button type="primary" @click="onSave">导出</el-button> <el-button type="danger" @click="closePage">取消</el-button> </div> </div> </template> <script> import { getTreeData } from '@/api/dataSchema' import { exportData,exportExcelData } from '@/api/conversion' import { Message } from 'element-ui' export default { name: 'Zzjg', inject: ['getList'], props: { proid: { type: String, required: true } }, data() { return { defaultProps: { children: 'children', label: 'name' }, treeData: [] } }, methods: { getDetailed() { const loading = this.$loading({ lock: true, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }) getTreeData().then(response => { this.treeData = response.data loading.close() }).catch(function() { loading.close() }) }, onSave() { var menuIds = this.$refs.tree.getCheckedKeys() if (menuIds.length === 0) { Message({ message: '请选择要导出的模型', type: 'error', duration: 5 * 1000 }) return } else { const loading = this.$loading({ lock: true, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }) exportExcelData({ menuIds: menuIds }).then(response => { var fileName = 'download.zip' const contentDisposition = response.headers['content-disposition'] if (contentDisposition) { fileName = window.decodeURI(response.headers['content-disposition'].split('=')[1], 'UTF-8') } const blob = new Blob([response.data], { type: `application/zip` // word文档为msword,pdf文档为pdf }) const objectUrl = URL.createObjectURL(blob) const link = document.createElement('a') link.href = objectUrl link.setAttribute('download', fileName) document.body.appendChild(link) link.click() // 释放内存 window.URL.revokeObjectURL(link.href) Message({ message: '导出成功', type: 'success', duration: 5 * 1000 }) loading.close() this.$emit('update:visible', false) this.getList() }).catch(response => { loading.close() }) } }, closePage() { this.$emit('update:visible', false) this.getList() } } } </script>
代码拆解:
通过前端传来的menuIds进行遍历,把每一条数据插到excel里面并且通过menuIds找到文件名与之对应的ktr文件放到文件夹中。
for (Long menuId : menuIds) { Conversion conversion = conversionMapper.selectById(menuId); if (conversion != null) { conversions.add(conversion); String ktrFilePath = fileService.getKtrFilePathById(menuId); if (ktrFilePath != null && !ktrFilePaths.contains(ktrFilePath)) { ktrFilePaths.add(ktrFilePath); } } }
创建excel导出模板:
// 创建Excel工作簿 HSSFWorkbook workbook = new HSSFWorkbook(); // 创建一个工作表 Sheet sheet = workbook.createSheet("Conversions"); // 创建单元格样式,并设置为文本格式 CellStyle textStyle = workbook.createCellStyle(); DataFormat format = workbook.createDataFormat(); textStyle.setDataFormat(format.getFormat("@")); // "@" 表示文本格式 // 创建标题行 Row titleRow = sheet.createRow(0); // 创建单元格样式,并设置为文本格式 titleRow.createCell(0).setCellValue("主键"); titleRow.createCell(1).setCellValue("分组id"); titleRow.createCell(2).setCellValue("名称"); titleRow.createCell(3).setCellValue("备注"); titleRow.createCell(4).setCellValue("创建人"); titleRow.createCell(5).setCellValue("关联状态"); titleRow.createCell(6).setCellValue("XMl"); titleRow.createCell(7).setCellValue("创建时间"); // 应用文本格式到标题行的特定单元格 titleRow.getCell(0).setCellStyle(textStyle); titleRow.getCell(1).setCellStyle(textStyle); // 填充数据 int rowNum = 1; for (Conversion conversion : conversions) { Row row = sheet.createRow(rowNum++); Cell cell = row.createCell(0); cell.setCellValue(String.valueOf(conversion.getId())); cell.setCellStyle(textStyle); cell = row.createCell(1); cell.setCellValue(String.valueOf(conversion.getGroupId())); cell.setCellStyle(textStyle); // 应用文本格式 row.createCell(2).setCellValue(conversion.getName()); row.createCell(3).setCellValue(conversion.getRemark()); row.createCell(4).setCellValue(conversion.getCreateUserName()); row.createCell(5).setCellValue(conversion.getState()); row.createCell(6).setCellValue(conversion.getEditXml()); row.createCell(7).setCellValue(String.valueOf(conversion.getCreateTime())); }
我这里的id和groupId位数特别长,所以对这两列做了默认为文本的处理,否则会变成科学计数法,会丢精。
具体如下:
// 创建单元格样式,并设置为文本格式 CellStyle textStyle = workbook.createCellStyle(); DataFormat format = workbook.createDataFormat(); textStyle.setDataFormat(format.getFormat("@")); // "@" 表示文本格式 // 应用文本格式到标题行的特定单元格 titleRow.getCell(0).setCellStyle(textStyle); titleRow.getCell(1).setCellStyle(textStyle); Row row = sheet.createRow(rowNum++); Cell cell = row.createCell(0); cell.setCellValue(String.valueOf(conversion.getId())); cell.setCellStyle(textStyle); cell = row.createCell(1); cell.setCellValue(String.valueOf(conversion.getGroupId())); cell.setCellStyle(textStyle); // 应用文本格式
把excel文件添加到压缩包:
// 将Excel文件添加到压缩包 ZipEntry excelEntry = new ZipEntry("conversions.xls"); zipOut.putNextEntry(excelEntry); workbook.write(zipOut); zipOut.closeEntry(); workbook.close();
把ktr文件放到ktr命名的文件夹中,并关闭压缩文件流
// 添加.ktr文件到ktr目录 for (String filePath : ktrFilePaths) { File ktrFile = new File(filePath); if (ktrFile.exists()) { FileInputStream fis = new FileInputStream(ktrFile); // 创建ZipEntry时,需要包含ktr目录 ZipEntry ktrEntry = new ZipEntry("ktr/" + ktrFile.getName()); zipOut.putNextEntry(ktrEntry); byte[] bytes = new byte[1024]; int length; while ((length = fis.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } fis.close(); zipOut.closeEntry(); } } // 完成压缩包 zipOut.finish(); zipOut.close();
导出就完成了。
导入
后端
导入的时候有一个要求,就是把导出时的id作为老的id存到数据库里,并生成新的id把新的id作为对应ktr的文件名存到对应的路径下面
解析数据:
@PostMapping("/insertData") public ResultData insertData(@RequestAttribute Long _userId, HttpServletRequest request) { MultipartHttpServletRequest req = (MultipartHttpServletRequest) request; MultipartFile uploadFile = req.getFile("uploadfile_ant"); String originalName = uploadFile.getOriginalFilename(); String docPath = ""; List<Long> codes = new ArrayList<>(); // 用于存储所有导入的 ID try { String classpath = ResourceUtils.getURL("classpath:").getPath(); String path = classpath + File.separator + "static" + File.separator + "file" + File.separator + "yulan"; docPath = path + File.separator + originalName; File dir = new File(path); if (!dir.exists()) { dir.mkdirs(); } // 保存压缩文件 File zipFile = new File(docPath); FileCopyUtils.copy(uploadFile.getInputStream(), new FileOutputStream(zipFile)); // 解压压缩文件 File unzipDir = new File(zipFile.getPath().substring(0, zipFile.getPath().lastIndexOf(".zip"))); if (!unzipDir.exists()) { unzipDir.mkdirs(); } unzipFile(zipFile, unzipDir); // 处理解压后的文件 processUnzippedFiles(unzipDir); return ResultData.success("ok", codes); // 返回所有导入的 ID 列表 } catch (Exception e) { e.printStackTrace(); return ResultData.error("error"); } }
解压代码:
private void unzipFile(File zipFile, File unzipDir) throws IOException { try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFile))) { ZipEntry entry = zipIn.getNextEntry(); while (entry != null) { String filePath = unzipDir.getPath() + File.separator + entry.getName(); if (!entry.isDirectory()) { extractFile(zipIn, filePath); } else { File dir = new File(filePath); dir.mkdirs(); } zipIn.closeEntry(); entry = zipIn.getNextEntry(); } } }
private void extractFile(ZipInputStream zipIn, String filePath) throws IOException { new File(filePath).getParentFile().mkdirs(); try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) { byte[] bytesIn = new byte[4096]; int read = 0; while ((read = zipIn.read(bytesIn)) != -1) { bos.write(bytesIn, 0, read); } } }
根据不同的文件类型去做不同的处理:
private void processUnzippedFiles(File unzipDir) throws Exception { // 遍历解压后的目录,处理每个文件 Files.walk(unzipDir.toPath()) .forEach(filePath -> { try { if (Files.isRegularFile(filePath) && filePath.toString().endsWith(".xls")) { //如果是excel文件 processExcelFile(filePath, unzipDir.toPath()); // } else if (Files.isDirectory(filePath) && "ktr".equals(filePath.getFileName().toString())) { // //ktr文件夹 // processKtrDirectory(filePath); } } catch (Exception e) { e.printStackTrace(); } }); }
处理excel:
@Transactional public void processExcelFile(Path filePath, Path zipPath) throws Exception { Workbook workbook = null; try { FileInputStream excelFile = new FileInputStream(filePath.toFile()); workbook = WorkbookFactory.create(excelFile); // 支持多种格式 // 假设我们只处理第一个工作表 Sheet sheet = workbook.getSheetAt(0); // 跳过标题行 int startRowIndex = 1; DataFormatter formatter = new DataFormatter(); for (int i = startRowIndex; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); if (row != null) { // 假设Excel文件的列顺序和数据库字段对应 Cell idCell = row.getCell(0); Cell groupIdCell = row.getCell(1); Cell NameCell = row.getCell(2); Cell remarkCell = row.getCell(3); Cell creatorCell = row.getCell(4); Cell xmlCell = row.getCell(6); // 检查空值和数据转换 String setOldId = formatter.formatCellValue(row.getCell(0)); String groupId = formatter.formatCellValue(row.getCell(1)); String remark = (remarkCell != null) ? remarkCell.getStringCellValue() : null; String Name = (NameCell != null) ? NameCell.getStringCellValue() : null; String creator = (creatorCell != null) ? creatorCell.getStringCellValue() :null; String state = formatter.formatCellValue(row.getCell(5)); String XML = (xmlCell != null) ? xmlCell.getStringCellValue() : null; // 创建一个数据对象,例如DataObject,并填充字段 Conversion conversion = new Conversion(); conversion.setId(SnowflakeIdGenerator.getId()); conversion.setOldId(Long.parseLong(setOldId)); conversion.setGroupId(Long.parseLong(groupId)); conversion.setName(Name); conversion.setRemark(remark); conversion.setCreateUserId(creator); conversion.setState(Integer.parseInt(state)); conversion.setEditXml(XML); conversion.setCreateTime(LocalDateTime.now()); // 保存到数据库 conversionMapper.insert(conversion); //ktr文件夹 processKtrDirectory(zipPath, conversion.getId(), conversion.getOldId()); } } } catch (Exception e) { throw new Exception("Error processing Excel file", e); } finally { if (workbook != null) { try { workbook.close(); } catch (IOException e) { // Log and handle workbook close exception } } } }
处理ktr:
private void processKtrDirectory(Path ktrDir, Long newId, Long oldId) throws Exception { // 处理ktr文件夹,将文件保存到磁盘路径下的逻辑 // 例如: String targetPath = ktrPath + File.separator + Constant.kettleScriptFileName + File.separator + Constant.ktrFileName + File.separator; String newPath = ktrDir.toString()+"/ktr"; try { Files.copy(Paths.get(newPath + File.separator + oldId.toString()), Paths.get(targetPath + newId.toString())); } catch (IOException e) { e.printStackTrace(); } }
用copy(source,target)就可以实现把文件保存到指定路径
fileService.getKtrFilePathById:
/** * 根据menuId获取对应的.ktr文件路径。 * @return 文件路径 */ @Service @Transactional public class FileServiceImpl implements FileService { @Value("${ktr.path}") private String ktrPath; public String getKtrFilePathById(Long menuId) { // 假设.ktr文件存储在 "/path/to/ktr/files/" 目录下,文件名为 "menuId.ktr" String baseDir = ktrPath + File.separator + Constant.kettleScriptFileName + File.separator + Constant.ktrFileName+File.separator; File file = new File(baseDir + menuId); if (file.exists()) { return file.getAbsolutePath(); } else { return null; // 或者抛出一个异常,表示文件不存在 } } }
前端:
<template> <div class="app-container" style="margin: 0 auto;width:100%;"> <el-form ref="form" label-width="80px" label-position="left"> <!-- <el-form-item> <div slot="label">分组<font color="red">*</font></div> <el-select v-model="form.groupId" placeholder="请选择分组" style="width: 100%"> <el-option v-for="item in fzList" :key="item.id" :label="item.name" :value="item.id" /> </el-select> </el-form-item> --> <!-- <el-form-item> <div slot="label">模型名称<font color="red">*</font></div> <el-input v-model="form.name" style="width:100%;" :autosize="{ minRows: 2, maxRows: 2}" /> </el-form-item> --> <el-form-item> <div slot="label">导入模型<font color="red">*</font></div> <el-upload accept=".zip" ref="upload" name="uploadfile_ant" class="upload-demo" :limit="1" :action="uploadpath" :headers="uoloadheaders" :before-upload="beforeAvatarUpload" :on-success="handleAvatarSuccess" :on-change="handleChange" :on-remove="handleRemove" :on-exceed="handleExceed" :file-list="fileList" > <el-button size="small" icon="el-icon-upload" type="primary">选择模型文件</el-button> <span style="color:red;"> 上传文件大小不能超过100MB</span> </el-upload> </el-form-item> <!-- <el-form-item label="备注:"> <el-input v-model="form.remark" type="textarea" maxlength="200" rows="6" placeholder="备注" /> </el-form-item> --> </el-form> <!-- <div style="text-align: center;width:100%;"> <el-button type="primary" @click="onSave">保存</el-button> <el-button type="danger" @click="closePage">取消</el-button> </div> --> </div> </template> <script> import { getWorkList } from '@/api/dataSchema' import { updateData } from '@/api/conversion' import { Message, MessageBox } from 'element-ui' import tool from '@/utils/tool' export default { name: 'Zzjg', inject: ['getList'], props: { proid: { type: String, required: true } }, data() { return { uploadpath: '', uoloadheaders: {}, fileData: '', // 文件上传数据(多文件合一) fileList: [], // upload多文件数组 fzList: [], form: {}, code: '' } }, methods: { getDetailed() { getWorkList().then(response => { this.fzList = response.data let address = process.env.NODE_ENV == 'development' ? process.env.VUE_APP_URL_RECON : process.env.VUE_APP_BASE_API; var path = '/ltcloud/conversion/insertData' this.uploadpath = address + path this.uoloadheaders = { 'X-TOKEN' : tool.getCookie('X-Token'), 'client-url':location.href, 'applicationId':this.applicationId } }) }, handleAvatarSuccess(res, file) { if (res.code === 20000) { this.code = res.data Message({ message: '上传成功', type: 'success', duration: 5 * 1000 }) } else { Message({ message: res.msg, type: 'error', duration: 5 * 1000 }) } }, // 移除 handleRemove(file, fileList) { this.fileList = fileList }, beforeAvatarUpload(file) { const isLt2M = file.size / 1024 / 1024 < 100 if (!isLt2M) { this.$message.error('上传文件大小不能超过100MB!') } return isLt2M }, // 选取文件超过数量提示 handleExceed(files, fileList) { this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`) }, // 监控上传文件列表 handleChange(file, fileList) { const existFile = fileList.slice(0, fileList.length - 1).find(f => f.name === file.name) if (existFile) { this.$message.error('当前文件已经存在!') fileList.pop() } this.fileList = fileList }, onSave() { console.log('分组不能为空') if (!this.form.groupId) { this.$message.error('分组不能为空') return } else if (!this.form.name) { this.$message.error('模型名称不能为空') return } else if (this.fileList.length === 0) { this.$message.error('导入模型不能为空') return } else { const loading = this.$loading({ lock: true, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }) this.form.idList = this.code updateData(this.form).then(response => { Message({ message: '编辑成功', type: 'success', duration: 5 * 1000 }) loading.close() this.$emit('update:visible', false) this.getList() }).catch(response => { loading.close() this.getList() }) } }, closePage() { this.$emit('update:visible', false) this.getList() } } } </script> <style lang="less"> /deep/ .el-dialog { width: 550px; height: 650px; } .displayCol { display: flex; } .newNum { height: 20px; width: 20px; border: 1px solid #333; border-radius: 50%; text-align: center; margin-top: 3px; line-height: 20px; } /deep/.el-form-item__label { text-align: left !important; padding: 0 10px; } .disabled-text { pointer-events: none; /* 阻止鼠标事件 */ cursor: default; /* 将鼠标光标设置为默认样式,表明文本不可点击 */ opacity: 0.5; /* 降低文本的不透明度以显示出它是不可交互的 */ user-select: none; /* 禁止文本被选中 */ } .el-upload-list { float: left; margin: 0; padding: 0; list-style: none; } .el-upload { margin-left: 0px; display: inline-block; text-align: center; cursor: pointer; outline: 0; } .el-upload__tip { font-size: 12px; color: #606266; margin-top: 7px; width: 300px; line-height: 45px; height: 10px; } </style>