比如页面上有一个 <input type="file" onChange="todo">
的选择器,我选择完以后绑定相对应的 onChange
事件。
function todo(){}
const fileReader = new FileReader()
const file=inputElement.files[0]
//接下来处理文件
//fileReader 的一些文件处理方法
if (file) {
fileReader.readAsDataURL(file)
}
fileReader.addEventListener(
'load',
() => {
const result = fileReader.result
const resultContainer = document.getElementById('result')
const img = document.createElement('img')
img.src = result
resultContainer.append(img)
},
{ once: true }
)
这种 FilerReader
实例化以后再进行读取操作的方法我在学习 go
这门语言中也见到过类似的设计。
问题:我好奇的点在于,为什么我们总要创建一个 fileReader
事例再去做一些事情?(为什么不直接给构造函数传参数 fileReader= new FileReader([file])
然后假设就可以在 fileReader
实例的属性上进行读取操作。比如:fileReader.result
、fileReader.readAsDataUrl
)
这种设计模式有什么好处吗?或者说 new FileReader(file)
直接传参有什么弊端吗?
回答:应该是JavaScript设计的WebApi和Java那种不一样,这个fileReader.read的相关操作都是异步的,写成调用函数传参的时候代入读取的内容,如file对象,可以比较好的在回调函数中使用这个对象,不然如果直接放在构造函数中去传入,重复使用这个fileReader就需要不停的重新调用构造函数,不免有些浪费了;而且这种基于事件回调类型的api很少通过构造函数直接传入,使用作用域大了,得考虑的事情会变多。
类与实例的设计,方便我们将类的状态保存在实例中,以备将来使用。并提供多样性的方法,满足不同需求。
换句话说,如果一个需求比较简单,一个动作就能完成,也没有太多衍生操作,那么做成函数就比较合适。比如 atob
,btoa
。反之,如果需求比较复杂,或者要覆盖更多的场景,可能类与实例的模式就更合适。比如你问题中的文件。你只用到一个方法,所以觉得函数化更方便;但是实际上需求会更多样,所以目前这样的设计更有价值。
为什么要实例化FileReader
再用?
实例化再用,更容易扩展逻辑。
比如你的例子,
const fileReader = new FileReader()
fileReader.readAsText(file)
fileReader.addEventListener('load', () => {
console.log(fileReader.result)
})
如果后续有人反馈,文件很大,读取文件需要很长时间,希望加进度条,有了FileReader
实例,我们就能方便地再加一个监听函数
const fileReader = new FileReader()
fileReader.readAsText(file)
fileReader.addEventListener('load', () => {
console.log(fileReader.result)
})
// 新增进度监听
fileReader.addEventListener('progress', (ev) => {
console.log(`${ev.loaded}/${ev.total}`)
})
如果后续又有人提出增加可以取消按钮,我们可以继续利用FileReader
实例,调用abort()
方法中断读取。
const fileReader = new FileReader()
fileReader.readAsText(file)
fileReader.addEventListener('load', () => {
console.log(fileReader.result)
})
// 新增进度监听
fileReader.addEventListener('progress', (ev) => {
console.log(`${ev.loaded}/${ev.total}`)
})
// 新增取消按钮
cancel.addEventListener('click', () => {
fileReader.abort()
}, { once: true })
由上可见,实例化FileReader
再操作,可以更灵活地利用FileReader
提供的接口,做更多的事。
然后再说说为什么不是直接new FileReader(file)
。
如果做过java
开发,会发现java
确实是这么干的。
public static void main(String[] args) {
char[] array = new char[100];
FileReader input = new FileReader("file.txt");
// Reads characters
input.read(array);
// Closes the reader
input.close();
}
以下是我的推测了,不一定正确。
首先JS
天生是异步的,文件的加载异步过程,一般不会设计在构造函数里。你肯定不会见过、也不允许写出这样的构造函数:
class MyClass {
// 报错 'async' modifier cannot appear on a constructor declaration
async constructor() {
}
}
当然,你也可以说,构造函数里并不立刻加载文件,可以在调用fileReader.readAsDataUrl()
再加载嘛。
这样的设计确实可行。但这需要考虑一点,就是避免不必要的理解成本。new FileReader(file)
再fileReader.readAsDataUrl()
,会给人造成错误的理解,误认为文件加载发生在构造函数中。如果换成new FileReader()
再fileReader.readAsDataUrl(file)
,那就能明确提示文件加载是发生在后一步里。
另外先new FileReader()
再fileReader.readAsDataUrl(file)
还有一点好处,就是可以单例复用,比如:
const input = document.getElementById('input')
// 只用一个实例
const fileReader = new FileReader()
input.onchange = () => {
const file = input.files?.item(0)
if (!file) return
fileReader.readAsText(file)
fileReader.onload = () => {
console.log(fileReader.result)
}
}
这样可以减少FileReader
的构造次数。
另外再提一下,FileReader
和XMLHttpRequest
差不多是一个年代的产物,那时候非常推崇OOP
(面向对象),所以“先实例化再调用方法”这种API设计也很流行。比如XMLHttpRequest
:
const req = new XMLHttpRequest()
req.open("GET", "http://www.example.org/example.txt")
req.addEventListener("load", () => {
console.log(req.responseText)
})
req.send()
是不是感觉跟FileReader
有几分相像?
不过现在有了fetch
,就不需要那么麻烦了。
const result = await fetch('http://www.example.org/example.txt').then(r => r.text())
console.log(result)
你可能会好奇FileReader
有没有替代的方案?
有,但要具体分析,取决于你要获取怎样的数据。
还是以<input type="file" id="input">
为例。
如果是加载文本:
const input = document.getElementById('input')
input.addEventListener('change', async () => {
const file = input.files?.item(0)
if (!file) return
// 没错,File继承自Blob,自带text()方法了
const text = await file.text()
console.log(text)
})
如果是加载图片:
/** @type {HTMLInputElement} */
const input = document.getElementById('input')
/** @type {HTMLImageElement} */
const img = document.getElementById('img')
input.addEventListener('change', () => {
const file = input.files?.item(0)
if (!file) return
// 创建一个虚拟URL给img标签使用
const blobURL = URL.createObjectURL(file)
img.src = blobURL
// img标签加载完成后,释放虚拟URL
img.addEventListener('load', () => {
URL.revokeObjectURL(blobURL)
}, { once: true })
})
在 JavaScript 中,FileReader
对象的设计是为了异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File
或 Blob
对象指定要读取的文件或数据。这种设计模式遵循了 JavaScript 中常见的异步编程模式,并且与 Web API 的其他部分保持一致。
为什么 FileReader
需要先实例化,然后再调用其方法来读取文件,而不是直接在构造函数中传入文件并立即读取,主要有以下几个原因:
FileReader
API 来异步处理这些操作,以避免阻塞主线程。如果 FileReader
允许在构造函数中直接传入文件并立即读取,那么它就需要同步地等待读取完成,这可能会导致用户界面无响应或延迟。FileReader
对象,你可以多次使用同一个对象来读取不同的文件,或者对同一个文件执行不同的读取操作(如 readAsText
、readAsDataURL
等)。这使得 FileReader
对象更加灵活和可复用。FileReader
API 是基于事件的。当你调用 readAsDataURL
、readAsText
等方法时,文件读取操作会立即开始,但结果并不会立即返回。相反,当读取操作完成时,会触发一个 load
事件,你可以通过监听这个事件来获取读取结果。这种基于事件的设计模式允许你在文件读取过程中执行其他操作,并在读取完成时得到通知。load
事件外,FileReader
还提供了 error
和 progress
等其他事件。通过监听这些事件,你可以处理读取过程中的错误或监视读取进度。如果将文件直接作为参数传递给 FileReader
构造函数,并在构造函数中立即读取文件,那么上述的所有优点都将不复存在。此外,直接在构造函数中读取文件也会破坏 JavaScript 的异步编程模型和事件驱动的设计模式。
因此,尽管直接在构造函数中传入文件并立即读取看起来可能更简洁,但这种设计并不符合 JavaScript 的异步编程模型和 Web API 的设计原则。通过先实例化 FileReader
对象,再调用其方法来读取文件,我们可以更好地利用 JavaScript 的异步性和事件驱动的特性,从而创建更加高效、灵活和可维护的代码。
由来 在FileUtil中本来已经针对文件的读操作做了大量的静态封装,但是根据职责分离原则,我觉得有必要针对文件读取单独封装一个类,这样项目更加清晰。当然,使用FileUtil操作文件是最方便的。 使用 在JDK中,同样有一个FileReader类,但是并不如想象中的那样好用,于是Hutool便提供了更加便捷FileReader类。 //默认UTF-8编码,可以在构造中传入第二个参数做为编码 Fi
本文向大家介绍Python 3.6 读取并操作文件内容的实例,包括了Python 3.6 读取并操作文件内容的实例的使用技巧和注意事项,需要的朋友参考一下 所使用python环境为最新的3.6版本 Python中几种对文件的操作方法: 将A文件复制到B文件中去(保持原来格式) 读取文件中的内容,返回List列表 (加载本地词典库) 读取文件,返回文件内容 以上这篇Python 3.6 读取并操作文
我已经完成了一些Java教程,它们都说在调用类时创建一个新变量。这是为什么?我已经测试了一些代码,但它没有这样做。我已经使用python很长一段时间了,所以我习惯于使用动态语言。 请看下面我一直在玩的一些代码: 谢谢你的时间。
我对java比较陌生,对如何使用缓冲读取器读取文件很好奇。这是因为我正在上一门课,被分配做一个简单的ceaser密码,我应该解密一个文本文件,创建一个新文件,并将解密的文本放入该文件。我可以用扫描仪和一个10KB的小文件来完成,但是当我要测试的100MB的大文本文件的时候,它是非常慢的。这是我的代码,它应该是读取文件内容。 如果有人能给我指明正确的方向,那就太好了。 提前致谢
本文向大家介绍JavaScript操作XML文件之XML读取方法,包括了JavaScript操作XML文件之XML读取方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了JavaScript操作XML文件之XML读取方法。分享给大家供大家参考。具体分析如下: 假设我们现在要读取下面的 info.xml 文件 接下来,读取并遍历info.xml 希望本文所述对大家的javascript程序设
1. 打开和关闭文件 1.1 打开文件 访问文件前,需要使用用 Python 内置的 open() 函数打开一个文件: open(path, access_mode) path 是要访问的文件的路径名 access_mode 是文件的访问模式 可以是只读、读写、追加等模式,所有可能的取值见 1.2 小节 这个参数是可选的,缺省情况下,是以只读模式 r 打开文件 open 返回一个 file 对象