一个关于FileReader读文件时有关编码问题的试验

通过实验对比FileReader默认编码与自定义编码读取GBK与UTF-8文件的效果,揭示Java中FileReader读取文本文件的实际编码方式。

由于在JAVA JDKAPI里个人认为FileReader的描述有些模糊,( 该类的构造函数假定默认字符编码和默认字节缓冲区大小是适当的),(也可能是渣翻版本本身的问题),搞不清楚

本文旨在测试:
FileReader究竟是以什么编码方式读取的文本

所以设计了以下试验


写方法

以GBK和UTF-8分别写了一个文本文件

/*
 * 类变量
 */
private static final String GBK_FILE = "txt_gbk.txt";
private static final String GBK = "gbk";

private static final String UTF8_FILE = "txt_utf8.txt";
private static final String UTF8 = "utf-8";

写方法的实现,往文件里,写了一个“你好呀”

	/**
	 * @throws IOException
	 */
	public static void write(String fileName,String charset) throws IOException {

		String str = "你好呀";
		
		//写
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName),charset));
		
		bw.write(str);
		bw.close();
	}

调用

	write(UTF8_FILE,UTF8);
	write(GBK_FILE,GBK);

在这里插入图片描述
utf-8的大小是9个字节,gbk是6个。因为utf8的中文编码是3个字节一个汉字


读方法

对比测试了默认读和 自定义读
默认读的方法是使用了FileReader读取字符流,自定义是用字节流转成的字符流,实现如下

	/**
	 * 默认读
	 * @throws IOException
	 */
	public static void showDefaultRead(String fileName) throws IOException {

	//默认读
	BufferedReader br = new BufferedReader(new FileReader(fileName));
	readLine(br);	
	}
	
	/**
	 * 自定义读
	 * @throws IOException
	 */
	public static void showCustomRead(String fileName,String charset) throws IOException {
		
	//自定义读
	BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName),charset));
	readLine(br);	
	}

	/**
	 * 操作流进行行读取
	 * @param br
	 * @throws IOException
	 */
	public static void readLine(BufferedReader br) throws IOException {
		String line = null;
		while((line=br.readLine())!=null) {
			System.out.println(line);
		}
		br.close();
	}

调用

		//默认读和用GBK的方式 同时读取GBK的文本文件
		showDefaultRead(GBK_FILE);
		showCustomRead(GBK_FILE,GBK);
		System.out.println();
		
		//默认读和用utf8的方式 同时读取GBK的文本文件
		showDefaultRead(GBK_FILE);
		showCustomRead(GBK_FILE,UTF8);
		System.out.println();
		
		//默认读和用GBK的方式 同时读取UTF8的文本文件
		showDefaultRead(UTF8_FILE);
		showCustomRead(UTF8_FILE,GBK);
		System.out.println();
		
		//默认读和用utf8的方式 同时读取UTF8的文本文件
		showDefaultRead(UTF8_FILE);
		showCustomRead(UTF8_FILE,UTF8);
		System.out.println();

输出结果

���ѽ
你好呀

���ѽ
���ѽ

你好呀
浣犲ソ鍛�

你好呀
你好呀

结论

明显可见,FileReader竟然是用UTF-8读取的文本文件?

结果其实和预想中有点不太一样,或者说还有什么本地的属性可以影响吗?还有待于确认


实验结果暂时就是这样了,有什么问题请不吝赐教,谢谢
<think>我们使用FileReader文件后生成临URL,通常使用URL.createObjectURL()方法,而不是直接通过FileReader。 但是,根据用户的问题,他可能误解了FileReader的用途。实际上,生成临URL不需要使用FileReader,而是使用URL.createObjectURL()方法,它接受一个Blob(或File)对象作为参数,并返回一个表示该对象的URL。 然而,用户明确提到“通过FileReader文件后生成一个有效的临地址”,这似乎暗示他可能已经尝试使用FileReader,并希望在取后得到临URL。 但请注意:FileReader用于文件内容(如文本、二进制数据等),而生成临URL并不需要文件内容,只需要文件对象(File对象,继承自Blob)即可。 因此,我们可以提供两种方案: 1. 直接使用URL.createObjectURL()生成临URL(推荐,无需文件内容,效率高) 2. 如果用户坚持使用FileReader,那么文件后得到的是数据(比如base64编码的字符串),我们可以将其转换为Data URL(但不是临URL,而是包含文件数据的字符串,可能会很长)。 根据引用[3]:File对象是特殊类型的Blob,可以用在任意Blob类型的context中,包括URL.createObjectURL()。 所以,我们分两种情况: 方案一(推荐):使用URL.createObjectURL()直接生成临URL(Blob URL) - 这个URL的生命周期和创建它的文档中的window绑定,在文档卸载自动释放。也可以手动释放(URL.revokeObjectURL())。 方案二:使用FileReader文件为Data URL(base64编码) - 这个URL是base64编码的字符串,可以直接嵌入到src属性(如图片),但字符串很长,不适合大文件。 下面分别给出两种方案的代码: ### 方案一:使用URL.createObjectURL()(推荐) ```javascript // 从input事件中获取文件 const file = e.target.files[0]; if (!file) return; // 生成临URL(Blob URL) const objectURL = URL.createObjectURL(file); console.log('临URL:', objectURL); // 例如:blob:http://example.com/550e8400-e29b-41d4-a716-446655440000 // 使用示例:显示图片 // const img = document.createElement('img'); // img.src = objectURL; // document.body.appendChild(img); // 注意:使用完后,在合适的机释放URL(比如图片加载完成后,或者组件卸载) // URL.revokeObjectURL(objectURL); ``` ### 方案二:使用FileReader取为Data URL(base64) 如果用户确实需要Data URL(比如需要嵌入到CSS或直接内联),可以使用FileReader的readAsDataURL方法。 ```javascript const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { // 取完成,result属性包含Data URL const dataURL = e.target.result; console.log('Data URL:', dataURL); // 例如:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg== // 使用示例:显示图片 // const img = document.createElement('img'); // img.src = dataURL; // document.body.appendChild(img); }; reader.onerror = function(error) { console.error('取失败:', error); }; reader.readAsDataURL(file); ``` ### 两种方案的比较 | 特性 | Blob URL (URL.createObjectURL) | Data URL (FileReader.readAsDataURL) | |--------------------|--------------------------------|-------------------------------------| | 生成方式 | 同步,立即返回 | 异步,需要等待取完成 | | 内存管理 | 需要手动释放(revokeObjectURL)| 无需释放,但字符串占用内存 | | 适用文件大小 | 支持大文件 | 不适合大文件(字符串很长) | | 格式 | blob:http://... | data:[<mediatype>][;base64],... | | 是否包含文件数据 | 不包含,只是一个引用 | 包含,base64编码整个文件 | | 跨域 | 同源策略 | 无跨域问题 | 通常情况下,如果只是需要在页面中引用(如图片、视频、下载链接等),使用Blob URL(方案一)是更好的选择,因为它不会将整个文件内容加载到内存中,而且对于大文件更高效。 如果用户的问题场景是需要在文件后生成一个地址,那么方案一更符合“临地址”的含义,因为它是临创建的URL,指向内存或磁盘上的文件对象,而方案二实际上是文件内容的base64编码。 因此,我们推荐方案一。 如果用户确实需要先文件(比如需要处理文件内容),然后再生成URL,那么他可能需要在文件后,将内容重新转换为Blob,然后再生成Blob URL。但这种情况较少见,因为如果不需要修改文件内容,直接使用原文件对象生成Blob URL即可。 下面是一个文件后修改内容再生成URL的例子(如果需要): ```javascript // 假设我们一个文本文件,修改内容后生成新的Blob URL const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { // 取文本内容 const text = e.target.result; // 修改内容(示例:在文本开头添加一行) const modifiedText = 'Modified:\n' + text; // 将修改后的文本转换为Blob const blob = new Blob([modifiedText], { type: file.type }); // 生成Blob URL const objectURL = URL.createObjectURL(blob); // 使用这个URL(比如创建一个下载链接) const a = document.createElement('a'); a.href = objectURL; a.download = 'modified_' + file.name; a.textContent = '下载修改后的文件'; document.body.appendChild(a); // 注意:在点击下载后,可以释放URL,或者设置定释放 // a.onclick = () => setTimeout(() => URL.revokeObjectURL(objectURL), 100); }; reader.readAsText(file); ``` ### 相关问题 1. Blob URL 和 Data URL 有什么区别? 2. 如何释放通过 URL.createObjectURL() 创建的临URL? 3. 使用 Blob URL 显示图片,如何确保在图片加载完成后释放URL? 4. 如果文件很大,使用 Data URL 会有什么问题? 5. 如何将 Data URL 转换为 Blob 对象? 请根据实际需求选择合适的方法。</think>### 使用 FileReader 生成临 URL 的完整指南 #### 核心解决方案:两种生成临 URL 的方式 1. **直接生成 Blob URL (推荐)** ```javascript const file = event.target.files[0]; if (!file) return; // 创建临 URL(无需文件内容) const objectURL = URL.createObjectURL(file); console.log("临URL:", objectURL); // blob:https://example.com/550e8400... // 使用示例:显示图片预览 const img = document.createElement('img'); img.src = objectURL; document.body.appendChild(img); // 使用后释放内存(重要!) // img.onload = () => URL.revokeObjectURL(objectURL); ``` - ✅ **优点**:同步操作、零延迟、内存高效 - ⚠️ **注意**:需手动释放 URL 防止内存泄漏[^3] 2. **通过 FileReader 生成 Data URL** ```javascript const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { // 生成 base64 编码的 Data URL const dataURL = e.target.result; console.log("Data URL:", dataURL); // data:image/png;base64,iVBORw0... // 使用示例 const img = document.createElement('img'); img.src = dataURL; document.body.appendChild(img); }; reader.readAsDataURL(file); ``` - ⚠️ **适用场景**:需要 base64 编码的小文件(<10MB) - ❌ **缺点**:内存占用高(base64 膨胀约33%),大文件性能差[^2] #### 关键区别解析 | 特性 | Blob URL (`URL.createObjectURL`) | Data URL (`FileReader.readAsDataURL`) | |--------------------|----------------------------------|---------------------------------------| | **生成机制** | 文件引用指针 | 完整的 base64 编码数据 | | **内存占用** | 低(仅存储引用) | 高(文件体积×1.33) | | **生成速度** | 即同步 | 异步取(文件越大越慢) | | **适用文件大小** | 无限制(推荐用于大文件) | 建议 <10MB | | **格式示例** | `blob:https://...` | `data:image/png;base64,...` | | **是否需要清理** | 是 (`URL.revokeObjectURL`) | 否(自动回收) | #### 最佳实践建议 1. **图片/视频预览首选 Blob URL** ```javascript // 安全释放URL的封装函数 function createTempURL(file, callback) { const url = URL.createObjectURL(file); callback(url); // 自动清理(可选超释放) return { url, revoke: () => URL.revokeObjectURL(url), autoRevoke: (timeout = 30000) => setTimeout(() => URL.revokeObjectURL(url), timeout) }; } // 使用示例 const tempURL = createTempURL(file, (url) => { document.getElementById('preview').src = url; }); // 手动释放:tempURL.revoke() ``` 2. **特殊场景使用 Data URL** ```javascript // 需要内联数据的场景(如CSS背景图) function createDataURL(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(file); }); } // 使用示例 createDataURL(file).then(dataURL => { document.body.style.background = `url(${dataURL})`; }); ``` #### 内存管理警告 ```javascript // 内存泄漏检测(控制台执行) setInterval(() => { console.log("当前Blob URL数量:", performance.memory.jsHeapSizeLimit - performance.memory.usedJSHeapSize); }, 5000); // 正确释放资源 window.addEventListener('beforeunload', () => { URL.revokeObjectURL(objectURL); // 页面关闭前清理 }); ``` #### 浏览器兼容性 | 方法 | Chrome | Firefox | Safari | Edge | IE | |--------------------------|--------|---------|--------|------|-----| | `URL.createObjectURL()` | 8+ | 4+ | 6+ | 10+ | 10+ | | `FileReader.readAsDataURL` | 7+ | 3.6+ | 6+ | 10+ | 10+ | > 对于 IE9 等老旧浏览器,可使用 `window.URL = window.URL || window.webkitURL;` 兼容[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值