三种方式实现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
相关推荐
小宋10212 小时前
实现Excel文件和其他文件导出为压缩包,并导入
java·javascript·excel·etl
金鸣科技官方QQ3 小时前
JSON数据转化为Excel及数据处理分析
json·excel
xisan_106 小时前
Excel把其中一张工作表导出成一个新的文件
excel·wps·使用技巧
Y.O.U..1 天前
力扣刷题-excel表名称序列相转换
算法·leetcode·excel
牛猫Data1 天前
提升数据分析效率:Excel Power Query和Power Pivot的妙用
microsoft·数据分析·excel·数据可视化·powerbi
admin⁠1 天前
php 导出excel 带图片
开发语言·php·excel
martian6651 天前
C# 基于WPF实现数据记录导出excel
开发语言·c#·excel
自由之翼Sai2 天前
Excel中超链接打开文件时报错 “打开此文件的应用程序没有注册“ 的一个解决办法
excel
糯米w2 天前
【前端】excel文件对比
前端·javascript·excel
CodeDevMaster2 天前
Python办公自动化:用xlrd轻松读取Excel文件
python·excel