三种方式实现mhtml格式转为excel

pdf转word, ppt转pdf等office,pdf格式相互转换都比较容易实现,比如可以用openoffice,onlyoffice等。但是mhtml(带有MIME附件的html文件)转excel,网上一搜可能没有那么多资料,本文将介绍三种方案将mhtml转为excel。

一.使用javax.mail解析mhtml

优点:可以按照自己的需要解析指定的内容和标签;

缺点:解析时整个文件是一次性加载到内存中,内存占用较大;需要自己解析指定的内容;

1.maven依赖

复制代码
<dependency>
	<groupId>javax.mail</groupId>
	<artifactId>mail</artifactId>
	<version>1.4.1</version>
</dependency>
<dependency>
	<groupId>org.jsoup</groupId>
	<artifactId>jsoup</artifactId>
	<version>1.14.3</version>
</dependency>

2.代码示例

java 复制代码
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

import javax.mail.BodyPart;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;


/**
 * description:MHTMLReader
 *
 * @author: lgq
 * @create: 2024-05-16 15:36
 */
public class MHTMLReader {
    public static void main(String[] args) {
        for (int i = 0; i< 100;i++) {
            int random = (int)(Math.random()*(11));
            System.out.println(random);
        }
        try {
            File mhtmlFile = new File("C:\\Users\\lgq\\Downloads\\003-r.mhtml");
            InputStream inputStream = new FileInputStream(mhtmlFile);
            MimeMessage mimeMessage = new MimeMessage(Session.getDefaultInstance(new Properties(), null), inputStream);
            // 解析MimeMessage对象,获取所需信息
            Object content = mimeMessage.getContent();
            if (content instanceof Multipart) {
                Multipart multipart = (Multipart) content;
                BodyPart bodyPart = multipart.getBodyPart(0);

                Object content1 = bodyPart.getContent();
                //获取内容
                String s = content1.toString();
                Document document = Jsoup.parse(s);
                Elements elements = document.select("tr");
                for (Element element : elements) {
                    String text = element.text();
                    System.out.println(text);
                }
            }
        } catch (Exception e ) {
            e.printStackTrace();
        }
    }
}
二.使用aspose解析mhtml

优点:可以直接将mhtml格式转为其他格式,比如xlsx;

缺点:内存占用非常大,多并发情况下很容易造成服务崩溃;

1.maven依赖

复制代码
<dependency>
	<groupId>com.aspose</groupId>
	<artifactId>aspose-cells</artifactId>
	<version>20.7</version>
	<scope>system</scope>
	<systemPath>${project.basedir}/src/main/resources/lib/aspose-cells-20.7-crack.jar</systemPath>
</dependency>

2.代码示例

java 复制代码
package com.abc.def.service.impl;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.concurrent.Future;

import javax.annotation.PostConstruct;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.aspose.cells.License;
import com.aspose.cells.LoadFormat;
import com.aspose.cells.LoadOptions;
import com.aspose.cells.MemorySetting;
import com.aspose.cells.Workbook;
import com.aspose.cells.Worksheet;
import com.abc.def.common.utils.EasyExcelUtil;
import com.abc.def.listener.EasyExcelGeneralCsvListener;
import com.abc.def.service.ExcelAnalysisService;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

/**
 * description:ExcelAnalysisService实现类
 *
 * @author: lgq
 * @create: 2024-04-17 14:53
 */
@Service
@Slf4j
public class ExcelAnalysisServiceImpl implements ExcelAnalysisService {

    @Async("asyncExcelAnalysisServiceExecutor")
    @Override
    public Future<String> csv2Excel(InputStream inputStream, OutputStream outputStream, String sheetName, boolean isGzip) {
        try {
            ExcelWriter writer = EasyExcel.write(outputStream).excelType(ExcelTypeEnum.XLSX).build();

            EasyExcel.read(inputStream, new EasyExcelGeneralCsvListener(writer, new WriteSheet()))
                    .excelType(ExcelTypeEnum.CSV)
                    .charset(Charset.forName("UTF-8"))
                    .sheet()
                    .sheetNo(0)
                    .sheetName(sheetName)
                    .doRead();

            writer.finish();
            outputStream.flush();
        } catch (Exception e) {
            log.error("csv转为excel出错!", e.getMessage());
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (Exception e) {
                    log.error("outputStream.close() -> csv转为excel出错!", e.getMessage());
                    e.printStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    log.error("inputStream.close() -> csv转为excel出错!", e.getMessage());
                    e.printStackTrace();
                }
            }
        }
        return new AsyncResult<>("task complete!");
    }

    @Async("asyncExcelAnalysisServiceExecutor")
    @Override
    public Future<String> mhtmlAsExcel(InputStream inputStream, OutputStream outputStream, String sheetName, boolean isGzip) {
        try {
            LoadOptions lo = new LoadOptions(LoadFormat.M_HTML); // 设置加载选项
            lo.setMemorySetting(MemorySetting.MEMORY_PREFERENCE);
            Workbook workbook = new Workbook(inputStream, lo);
            workbook.getWorksheets().get(0).setName(sheetName); // 设置工作簿的名称
            workbook.getWorksheets().get(0).setGridlinesVisible(true); // 显示网格线
            workbook.getWorksheets().get(0).autoFitColumns();// 设置自适应列宽
            workbook.getWorksheets().get(0).autoFitRows();// 设置自适应行高
            workbook.save(outputStream, LoadFormat.XLSX);
        } catch (Exception ioException) {
            log.error("workbook.write() -> 另存为excel出错!", ioException.getMessage());
            ioException.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (Exception e) {
                    log.error("outputStream.close() -> csv转为excel出错!", e.getMessage());
                    e.printStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    log.error("inputStream.close() -> csv转为excel出错!", e.getMessage());
                    e.printStackTrace();
                }
            }
        }
        return new AsyncResult<>("task complete!");
    }

    @PostConstruct
    public void initAspose() {
        try (InputStream is = com.aspose.cells.License.class.getResourceAsStream("/com.aspose.cells.lic_2999.xml")) {
            License asposeLicense = new License();
            asposeLicense.setLicense(is);
        } catch (Exception e) {
            log.error("initAspose -> 初始化加载license文件失败!", e.getMessage());
            e.printStackTrace();
        }
    }
}

3.aspose jar包参考

从gitee上找到了一位大佬的学习实践教程,作者研究了aspose.cells的 20.7 的版本,距离最新版本发布相距 3 年多近 40 个Release版本,可参考:https://gitee.com/evilrule/crack-aspose,有需要旧版的可以移步查阅和实践,确实可以把《Evaluation Warning》警告去除;但是不可以进行商用

三.使用windows的office.exe解析mhtml(模拟打开另存为xlsx操作)

优点:把mhtml格式转换转发给office软件执行,java服务没有内存压力;

缺点:为了更换支持格式转换,一般需要部署在windows服务器上;

1.maven依赖

无。

2.代码示例

java 复制代码
package com.abc.def.handler;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import com.abc.def.common.utils.EasyExcelUtil;
import com.abc.def.common.utils.ObjectUtil;
import com.abc.def.pojo.enums.ConversionType;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import static com.abc.def.common.utils.EasyExcelUtil.gzFileExtractor;
import static com.abc.def.pojo.enums.EpDownloadErrorCode.FILE_TOO_LARGE;
import static com.abc.def.pojo.enums.EpDownloadErrorCode.FILE_TYPE_ERROR;
import static com.abc.def.pojo.enums.EpDownloadErrorCode.FREQUENCY_TOO_HIGH_ERROR;

/**
 * description:mhtml转为excel
 *
 * @author: lgq
 * @create: 2024-05-17 11:27
 */
@Slf4j
@Component("mhtml2ExcelConversion")
public class Mhtml2ExcelConversion implements ConversionFunction {
    private static final String PS1_PATH = "D:\\ep\\mhtml2excel1.ps1";
    @Override
    public String converse(MultipartFile file, HttpServletResponse response) {
        String filename = file.getOriginalFilename();
        String fileType = "MHTML";
        String[] splitName = filename.split(".xls");
        if (ObjectUtil.isEmpty(splitName) || ObjectUtil.isEmpty(splitName[0])) {
            String errorMsg = "文件格式不对,请检查格式!";
            EasyExcelUtil.setResponseParam(response, "Process-Result", String.valueOf(FILE_TYPE_ERROR.getCode()));
            EasyExcelUtil.setResponseParam(response, "Content-Type", "text/plain");
            return errorMsg;
        }
        String pureFilename = splitName[0];
        EasyExcelUtil.setResponseParam(response, pureFilename);

        File tempFile = null;
        String targetFilePath = null;
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            String tempFilePath = gzFileExtractor(inputStream);
            if (!ObjectUtil.isEmpty(tempFilePath) && "false".equals(tempFilePath)) {
                tempFilePath = UUID.randomUUID().toString();
                tempFile = new File(tempFilePath);
                if (!tempFile.exists()) {
                    tempFile.createNewFile();
                }
                tempFilePath = tempFile.getAbsolutePath();
                try (InputStream inputStream1 = file.getInputStream()) {
                    Files.copy(inputStream1, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                } catch (Exception e) {
                    log.error("MultipartFile 转为 File 失败!(mhtml)", e.getMessage());
                    e.printStackTrace();
                }
            } else {
                tempFile = new File(tempFilePath);
            }
            long fileLength = tempFile.length();

            log.info("the length of origin file is {} ", fileLength); //172460518
            if (fileLength > 100000000) {
                String errorMsg = "导出excel文件超过128M,请缩小查询范围后再进行导出!";
                EasyExcelUtil.setResponseParam(response, "Process-Result", String.valueOf(FILE_TOO_LARGE.getCode()));
                EasyExcelUtil.setResponseParam(response, "Content-Type", "text/plain");
                return errorMsg;
            }
            targetFilePath = tempFilePath + ".xlsx";
            Future<String> future = mhtmlAsExcel(tempFilePath, targetFilePath, response);
            return future.get();
        } catch (IOException ioException) {
            log.error("{}转为excel出错!", fileType, ioException.getMessage());
            ioException.printStackTrace();
        } catch (InterruptedException interruptedException) {
            log.error("{}转为excel出错!", fileType, interruptedException.getMessage());
            interruptedException.printStackTrace();
        } catch (ExecutionException executionException) {
            log.error("{}转为excel出错!", fileType, executionException.getMessage());
            executionException.printStackTrace();
        } catch (RuntimeException x) {
            log.error("{}转为excel出错!", fileType, x.getMessage());
            x.printStackTrace();
        } catch (Error x) {
            log.error("{}转为excel出错!", fileType, x.getMessage());
            x.printStackTrace();
        } catch (Throwable x) {
            log.error("{}转为excel出错!", fileType, x.getMessage());
            x.printStackTrace();
        } finally {
            if (tempFile != null && tempFile.delete()) {
                log.info("原始文件{}删除成功!", tempFile.getName());
            } else if (tempFile != null) {
                log.error("原始文件{}删除失败!", tempFile.getName());
            }
            try {
                if (!ObjectUtil.isEmpty(targetFilePath)) {
                    File targetFile = new File(targetFilePath);
                    if (targetFile.exists() && targetFile.delete()) {
                        log.info("转换后的文件{}删除成功!", targetFile.getName());
                    } else if (targetFile.exists()) {
                        log.error("转换后的文件{}删除失败!", targetFile.getName());
                    }
                }

            } catch (Exception e) {
                log.error("targetFile.delete() -> 删除转换后的文件出错!", e.getMessage());
                e.printStackTrace();
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    log.error("inputStream.close() -> csv转为excel出错!", e.getMessage());
                    e.printStackTrace();
                }
            }
        }
        return "success";
    }


    @Async("asyncExcelAnalysisServiceExecutor")
    public Future<String> mhtmlAsExcel(String originFileName, String targetFileName, HttpServletResponse response)
            throws Exception {
        String powershellCommand = "powershell.exe "+ PS1_PATH +" " + originFileName + " " + targetFileName;
        try {
            // 定义PowerShell 命令
            String baseShellCommand = "Get-Process | Select-Object -First 5";
            // 创建ProcessBuilder 实例
            ProcessBuilder processBuilder = new ProcessBuilder("powershell", "-Command", powershellCommand);
            //启动进程
            Process process = processBuilder.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), Charset.forName("GBK")));
            String line;
            int errorLogLength = 0;
            while ((line = reader.readLine()) != null) {
                log.error(line);
                errorLogLength += line.length();
                break;
            }
            if (errorLogLength > 0) {
                String errorMsg = "mhtml转换为excel过于频繁,请稍后再试!";
                log.error(errorMsg);
                process.destroy();
                EasyExcelUtil.setResponseParam(response, "Process-Result", String.valueOf(FREQUENCY_TOO_HIGH_ERROR.getCode()));
                EasyExcelUtil.setResponseParam(response, "Content-Type", "text/plain");
                return new AsyncResult<>(errorMsg);
            }
            //等待进程结束并获取退出码

            int exitCode = process.waitFor();
            if (exitCode == 0) {
                log.info("Exited with error code :{} ", exitCode);
                File file = new File(targetFileName);
                if (file.exists()) {
                    // 读取本地文件
                    try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
                         ServletOutputStream outputStream = response.getOutputStream()) {
                        byte[] buffer = new byte[1024];
                        int bytesRead;
                        // 将文件内容写入响应
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                        outputStream.flush();
                    } catch (Exception e) {
                        log.error("读取mhtml转为excel后的xlsx文件执行出错!", e.getMessage());
                        throw new Exception(e);
                    }
                }
            } else {
                log.error("Exited with error code :{} ", exitCode);
            }

        } catch (Exception e) {
            log.error("mhtml转为excel的shell脚本执行出错!", e.getMessage());
            throw new Exception(e);
        }
        return new AsyncResult<>("success");
    }

    @Override
    public ConversionType getType() {
        return ConversionType.CONVERSION_MHTML2EXCEL;
    }
}

3.附件ps脚本

bash 复制代码
param($s,$d)
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$workbook = $excel.Workbooks.Open($s)
$workbook.SaveAs($d, 51) # 51 represents Excel Workbook format (.xlsx)
$workbook.Close()
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook) 
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) 
Remove-Variable excel,workbook
相关推荐
E-iceblue2 天前
Excel 列转行/行列转换全指南:从 4 种常见解法到 Python 批量自动化
python·excel·python库·spire.xls
Data-Miner2 天前
AI处理数据的脚本复用与逻辑复用有什么区别?哪种方式更能沉淀工作成果
数据分析·excel
福兮说2 天前
在浏览器里写一个“够用就好“的 Excel 公式求值器
前端·javascript·excel·编译原理
慧都小妮子3 天前
标签打印如何减少手工抄数?用Bartender 直连 Excel 批量打印
excel·bartender·条码打印·标签打印软件·数据驱动打印·工业打印机
SunnyDays10113 天前
Java 设置 Excel 数据验证:整数、日期、文本长度、下拉列表和时间
java·excel·数据验证·下拉列表
Metaphor6923 天前
使用 Python 设置 Excel 行列自适应 【代码示例】
python·excel
用户0332126663674 天前
使用 Python 设置 Excel 行列自适应 【代码示例】
python·excel
企业数字化笔记4 天前
固定资产Excel批量导入怎么防重复?资产编码、重复检查和错误回执
java·后端·excel
CORNERSTONE3654 天前
低代码开发有哪些应用场景?
低代码·excel·erp·mes·企业数字化
小葱炖豆腐5 天前
python绘制excel折线图
python·excel·numpy·pandas·matplotlib