NO.63 [file]IO常用工具类IOUtils(Java读文件、写文件、打Zip包)

本文介绍了一个实用的Java IO工具类,提供了多种文件操作方法,包括文件读取、转换及压缩等。通过这些方法,可以方便地进行文件的读写、转换为字节流、按指定编码读取文件内容等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

功能目录:

  1. 将输入流转换成字节流
  1. 将文件读取为一个字符串
  1. 以指定编码格式将输入流按行置入一个List<String>
  1. 以GBK格式将输入流按行置入一个List<String>
  1. 转换为每行补充指定换行符(例如:"\n","</br>")
  1. 将字符串转出到指定文件
  1. 将多个文件打成一个Zip包

 

源码:

package amosryan.utility.file;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * IO常用工具包
 * @author amosryan
 * @since 2010-06-03
 */
public class IOUtils {

	/**
	 * 将输入流转换成字节流
	 * @param input
	 * @return
	 * @throws Exception
	 */
	public static byte[] toBytes(InputStream input) throws Exception {
		byte[] data = null;
		try {
			ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
			byte[] b = new byte[1024];
			int read = 0;
			while ((read = input.read(b)) > 0) {
				byteOut.write(b, 0, read);
			}
			data = byteOut.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			input.close();
		}
		return data;
	}

	/**
	 * 将文件读取为一个字符串
	 * 
	 * @param input
	 * @return
	 * @throws Exception
	 */
	public static String toString(File file) throws Exception {
		return toString(new FileInputStream(file));
	}

	/**
	 * 将输入流转换为一个串
	 * 
	 * @param input
	 * @return
	 * @throws Exception
	 */
	public static String toString(InputStream input) throws Exception {
		return toStringWithLineBreak(input, null);
	}

	/**
	 * 以指定编码格式将输入流按行置入一个List<String>
	 * 
	 * @param input
	 * @return
	 * @throws Exception
	 */
	public static List<String> toLines(InputStream input, String encoding)
			throws Exception {
		InputStreamReader insreader = new InputStreamReader(input, encoding);
		BufferedReader bin = new BufferedReader(insreader);
		List<String> lines = new ArrayList<String>();
		String line;
		while ((line = bin.readLine()) != null) {
			lines.add(line);
		}
		bin.close();
		insreader.close();
		return lines;
	}

	/**
	 * 以GBK格式将输入流按行置入一个List<String>
	 * 
	 * @param input
	 * @return
	 * @throws Exception
	 */
	public static List<String> toLines(InputStream input) throws Exception {
		return toLines(input, "GBK");
	}

	/**
	 * 转换为每行补充指定换行符(例如:"/n","</br>")
	 * 
	 * @param input
	 * @param lineBreak
	 * @return
	 * @throws Exception
	 */
	public static String toStringWithLineBreak(InputStream input,
			String lineBreak) throws Exception {
		List<String> lines = toLines(input);
		StringBuilder sb = new StringBuilder(20480);
		for (String line : lines) {
			sb.append(line);
			if (lineBreak != null) {
				sb.append(lineBreak);
			}
		}
		return sb.toString();
	}

	/**
	 * 将字符串转出到指定文件
	 * @param saveFile
	 * @param content
	 */
	public static void toFile(File saveFile, String content) {
		File parent = saveFile.getParentFile();
		if (!parent.exists()) {
			parent.mkdirs();
		}
		PrintWriter out = null;
		try {
			out = new PrintWriter(new FileWriter(saveFile));
			out.print(content);
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				out.close();
			}
		}
	}

	/**
	 * 将一组文件打zip包
	 * 
	 * @param srcFiles
	 * @param targetFileName
	 * @throws IOException
	 */
	public static void filesToZip(List<File> srcFiles, String targetFileName)
			throws IOException {
		String fileOutName = targetFileName + ".zip";
		byte[] buf = new byte[1024];
		FileInputStream in = null;
		FileOutputStream fos = null;
		ZipOutputStream out = null;
		try {
			fos = new FileOutputStream(fileOutName);
			out = new ZipOutputStream(fos);
			for (File file : srcFiles) {
				in = new FileInputStream(file);
				out.putNextEntry(new ZipEntry(file.getName()));
				int len;
				while ((len = in.read(buf)) != -1) {
					out.write(buf, 0, len);
				}
				if (in != null) {
					in.close();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (in != null) {
				in.close();
			}
			if (fos != null) {
				out.closeEntry();
				out.close();
				fos.close();
			}
		}
	}

	public static void main(String[] args) {
		try {
			File doc1 = new File(
					"E://workspace//test//doc//1272531757100_1.doc");
			IOUtils.toString(new FileInputStream(doc1));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


15:31:54.142 [main] DEBUG com.alibaba.excel.metadata.property.ExcelHeadProperty - The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is NONE 15:31:54.168 [main] DEBUG com.alibaba.excel.context.AnalysisContextImpl - Initialization 'AnalysisContextImpl' complete Exception in thread "main" com.alibaba.excel.exception.ExcelAnalysisException: java.lang.OutOfMemoryError: Java heap space at com.alibaba.excel.analysis.ExcelAnalyserImpl.<init>(ExcelAnalyserImpl.java:61) at com.alibaba.excel.ExcelReader.<init>(ExcelReader.java:30) at com.alibaba.excel.read.builder.ExcelReaderBuilder.build(ExcelReaderBuilder.java:214) at com.alibaba.excel.read.builder.ExcelReaderBuilder.sheet(ExcelReaderBuilder.java:251) at com.alibaba.excel.read.builder.ExcelReaderBuilder.sheet(ExcelReaderBuilder.java:243) at com.soyotec.unixde.dtm.common.impl.datafile.handler.ExcelDataHandler.readPage(ExcelDataHandler.java:37) at com.soyotec.unixde.dtm.common.impl.datafile.Test.main(Test.java:27) Caused by: java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3236) at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118) at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93) at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153) at org.apache.poi.util.IOUtils.toByteArray(IOUtils.java:155) at org.apache.poi.util.IOUtils.toByteArray(IOUtils.java:121) at org.apache.poi.util.IOUtils.toByteArray(IOUtils.java:108) at org.apache.poi.openxml4j.util.ZipArchiveFakeEntry.<init>(ZipArchiveFakeEntry.java:47) at org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource.<init>(ZipInputStreamZipEntrySource.java:53) at org.apache.poi.openxml4j.opc.ZipPackage.openZipEntrySourceStream(ZipPackage.java:210) at org.apache.poi.openxml4j.opc.ZipPackage.openZipEntrySourceStream(ZipPackage.java:194) at org.apache.poi.openxml4j.opc.ZipPackage.openZipEntrySourceStream(ZipPackage.java:168) at org.apache.poi.ope
03-26
<think>好的,我现在需要帮助用户解决Java程序中处理Excel大文件时出现的OutOfMemoryError问题,特别是使用com.alibaba.excel(EasyExcel)时的情况,以及Apache POI的优化方法。首先,我得回顾一下用户提供的引用内容,看看有哪些相关信息。 用户提到,当处理大文件时,POI的传统WorkBook方式会因为将整个文件加载到内存而导致堆内存溢出。而POI官方推荐使用XSSF和SAX的事件驱动API来处理,这应该是基于流式取,逐行处理,避免一次性加载整个文件。另外,用户还提到阿里云的EasyExcel,它似乎也是基于似的思想,可能对内存做了优化。 首先,分析问题原因。OutOfMemoryError通常是因为处理大文件时,一次性加载所有数据到内存中,导致堆空间不足。所以解决方案的核心应该是减少内存占用,使用流式取或分片处理。 接下来,我需要结合引用中的信息来整理解决方案。对于Apache POI,用户引用[2]和[3]提到使用SXSSFWorkBook来处理入,而取大文件则需要用XSSF和SAX的事件API。这可能涉及到像逐行解析,而不是构建整个DOM树。 对于EasyExcel,用户引用[3]和[4]提到阿里推荐使用它来处理大文件,可能因为它内部实现了似SAX的解析方式,或者分批次处理数据。需要具体说明如何通过EasyExcel避免内存溢出,比如使用逐行取的回调机制。 然后,我需要分步骤给出解决方案,可能分为POI优化和EasyExcel优化两部分。每个部分要详细说明如何配置和使用,比如调整JVM内存参数、使用特定的和方法、示例代码片段等。同时,需要注意用户提到的依赖,引用[4]中的EasyExcel和Fastjson版本,可能需要指导用户正确引入依赖。 另外,用户可能对两种方式(POI和EasyExcel)的优缺点不太清楚,需要比较并推荐合适的场景。例如,POI的SAX方式虽然高效,但可能编码复杂,而EasyExcel封装得更好,使用更简便。 最后,生成相关问题,帮助用户深入理解,比如如何选择工具、其他内存优化技巧、原理比较等。需要确保问题覆盖用户可能感兴趣的扩展方向。 现在需要检查是否有遗漏点。例如,用户提到com.alibaba.excel出现OutOfMemoryError,这可能是因为即使使用EasyExcel,如果处理不当(如一次性处理所有数据),仍然可能出问题。因此,需要强调使用正确的取方式,如逐行处理,避免在内存中累积数据。 总结下来,解决方案应括: 1. 调整JVM堆内存参数,作为临时解决方案。 2. 使用Apache POI的SXSSFWorkBook入,使用事件API取。 3. 使用EasyExcel的流式取和入,通过监听器逐行处理。 4. 比较两种方法的优缺点,推荐最佳实践。 确保每个步骤都有代码示例或配置示例,并引用用户提供的资料作为支持。例如,引用[3]中提到SXSSFWorkBook的处理机制,以及EasyExcel的优势。 现在需要组织语言,用中文清晰、结构分明地呈现这些内容,确保符合用户要求的格式,如行内公式用$...$,独立公式用$$...$$,但可能在这个问题中数学公式较少,可以适当忽略。同时,生成的相关问题要围绕核心主题,帮助用户进一步学习。</think>### 解决Java处理Excel大文件时的OutOfMemoryError问题 #### 一、问题原因 当处理大Excel文件时,传统方式(如Apache POI的`WorkBook`)会一次性将数据加载到内存中,形成树状结构(如`TreeMap`存储行数据),导致内存占用过高,触发`java.lang.OutOfMemoryError: Java heap space`错误[^2][^3]。 --- #### 二、解决方案 ##### 1. **调整JVM堆内存(临时缓解)** - 通过增大JVM堆内存参数缓解问题,但无法根本解决大文件处理问题。 ```bash java -Xms512m -Xmx4096m -jar YourApp.jar ``` ##### 2. **Apache POI优化方案** - **入大文件**:使用`SXSSFWorkBook`,按阈值分批次入磁盘。 ```java SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 每100行刷新到磁盘 Sheet sheet = workbook.createSheet(); for (int i = 0; i < 100000; i++) { Row row = sheet.createRow(i); // 入数据... } workbook.write(new FileOutputStream("output.xlsx")); workbook.dispose(); // 清理临时文件 ``` - **取大文件**:使用`XSSF + SAX`事件驱动API逐行解析。 ```java OPCPackage pkg = OPCPackage.open(new File("input.xlsx")); XSSFReader reader = new XSSFReader(pkg); XMLReader parser = XMLReaderFactory.createXMLReader(); parser.setContentHandler(new SheetHandler()); // 自定义处理器 parser.parse(reader.getSheetsData().next()); ``` ##### 3. **使用EasyExcel(阿里推荐)** - **核心原理**:基于事件驱动和逐行解析,内存占用低[^4]。 - **依赖引入**: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.4</version> </dependency> ``` - **取大文件示例**: ```java // 定义数据模型 public class UserData { @ExcelProperty("姓名") private String name; // 其他字段... } // 注册监听器逐行处理 EasyExcel.read("input.xlsx", UserData.class, new AnalysisEventListener<UserData>() { @Override public void invoke(UserData data, AnalysisContext context) { // 处理单行数据,避免累积内存 } @Override public void doAfterAllAnalysed(AnalysisContext context) { // 完成处理 } }).sheet().doRead(); ``` - **入大文件示例**: ```java List<UserData> dataList = fetchData(); // 分批次获取数据 EasyExcel.write("output.xlsx", UserData.class) .registerWriteHandler(new SXSSFSheetWriteHandler(100)) // 每100行刷新 .sheet().doWrite(dataList); ``` --- #### 三、方案对比与推荐 | 方法 | 优点 | 缺点 | 适用场景 | |--------------|-------------------------------|-------------------------------|------------------------| | **Apache POI** | 官方支持,功能全面 | 编码复杂,需手动管理内存 | 需要精细控制底层逻辑 | | **EasyExcel** | 内存优化好,API简洁 | 依赖第三方库 | 大数据量快速处理 | **推荐优先使用EasyExcel**,尤其适合数据量超过10万行的场景[^3]。 --- #### 四、扩展优化技巧 - **分批次处理**:将大文件拆分为多个小文件处理。 - **避免全表缓存**:在监听器中直接处理数据,而非存储到`List`。 - **关闭无用资源**:及时调用`dispose()`或关闭流。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值