比较佩服nodejs的文档, 真心粗糙啊.

原理

  • 首先说fs.createReadStream弄的是一个readable stream
  • readable stream 也是一个stream
  • stream 就是EventEmitter
  • 一切都是文件, 不论键盘, 显示器, 都是文件.
  • 一切文件都是流, 实际上都是当做流来处理的. 都是有open, pause, read, close…..等等操作和类似ondata, onend….这样的事件

问题的关键不在原理, 在细节

原型: fs.createReadStream(path[, options])

path, 可以是这三样:

  • string
  • buffer
  • url

options,

  • string 特指encode – 参见下面encode的说明
  • Object 包含以下8项内容
  1. flags string
  2. encoding string
  3. fd int
  4. mode int
  5. autoClose bool
  6. start int
  7. end int
  8. highWaterMark int

就这8项的详解, 好难找啊.

flags

  • 这个是读写的模式
  • 写入的时候更有用.
  • 比如w就是覆盖写
  • r+ 就是添加写

fd

  • 这个项目会导致忽略很多项目:
    • start
    • path也会被忽略
  • file descriptor
    • POSIX 标准规定的
    • stdout, stdin and stderr 的编码是0, 1, 2
    • 一切都是文件, 就是这个货.
  • https://en.wikipedia.org/wiki/File_descriptor
  • https://stackoverflow.com/questions/36771266/what-is-the-use-of-fd-file-descriptor-in-node-js/36771339

mode

  • 是linux permission
  • 只有新建文件的时候有效, 换句话说, 放到这里就和前面的flag一样, 其实一般情况是没用的.
  • 那么啥时候有用呢? 我不知道, 有知道的告诉我一声.

autoclose

  • 如果他是false, 那么出错和end, 就不会自动关闭流, 他对下面两个事件有影响
    1. error
    2. end
  • 这是第一个明确有用的内容

encoding

  • buffer可以使用的encoding都是他的取值范围, 以下8种

    1. ascii 7bit模式 高位的1个bit会被忽略

    2. utf8 1-多byte模式

    3. utf16le 2-4byte

    4. ucs2 和上面的utf16le是一回事

    5. base64 url和filename安全

    6. latin1 单byte模式

    7. binary 和latin1是一样的

    8. hex 两byte模式

  • 参考:https://nodejs.org/api/buffer.html#buffer_class_method_buffer_from_string_encoding

start end

  • 设置读取的文件位置.
  • 起止点都包含在读取范围之内.
  • 是否可以使用负数?
  • 是否可以直接指定读取尾部的128字节?
//例子, 读取一个100字节的文件的最后十个字节
fs.createReadStream('sample.txt', { start: 90, end: 99 });

highwatermark

  • 这个项目控制的是chunk的size
  • 例如: var rs = fs.createReadStream(‘/foo/bar’, { highWaterMark: 128 * 1024 });
  • 参见: https://stackoverflow.com/questions/27641571/changing-readstream-chunksize

官网啥都有, 但是, 结构上很晕.

  • https://nodejs.org/api/fs.html

基础内容

  • 建议看官网, 这里粗略记录一点, 比如
  • 两种模式: 流模式stream和暂停模式pause
  • 暂停切换到流模式的三个方法
    • data事件处理器
    • 显式调用resume
    • 调用pipe
  • 流切到暂停
    • 没有用pipe的情况下, pause即可
    • 用了pipe, 那么要做两步
      • 溢出data事件的处理器
      • 用unpipe断开所有管道
  • 各种基础的参数, 函数….
  • 异步方法如果要保证顺序, 就要把调用链起来
  • csdn这个文章也不错: http://blog.csdn.net/foruok/article/details/49120183

基础函数

  • fs.open
  • fs.stat

基础数据结构

  • buffer
  • fs.stats

stats

  • 是一个json字符串
  • http://nodejs.cn/api/fs.html
  • fs.stat()fs.lstat()fs.fstat()的返回值, 这三个函数只有微妙的不同, node文档并无解释, 解释在linux文档: http://man7.org/linux/man-pages/man2/lstat.2.html
  • util.inspect(stats)会返回stats的描述字符串. util是node的一个类库: http://nodejs.cn/api/util.html#util_util_inspect_object_options
//util.inspect返回
Stats {
  dev: 2114,
  ino: 48064969,
  mode: 33188,
  nlink: 1,
  uid: 85,
  gid: 100,
  rdev: 0,
  size: 527,
  blksize: 4096,
  blocks: 8,
  atimeMs: 1318289051000.1,
  mtimeMs: 1318289051000.1,
  ctimeMs: 1318289051000.1,
  birthtimeMs: 1318289051000.1,
  atime: Mon, 10 Oct 2011 23:24:11 GMT,
  mtime: Mon, 10 Oct 2011 23:24:11 GMT,
  ctime: Mon, 10 Oct 2011 23:24:11 GMT,
  birthtime: Mon, 10 Oct 2011 23:24:11 GMT }
//函数
stats.isFile()
stats.isDirectory()
stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink() (仅对 fs.lstat() 有效)
stats.isFIFO()
stats.isSocket()
  • 例如: stats.size就是文件的尺寸(Bytes)
  • 或者stats[‘size’]

fs感悟:

  • fs就是照搬了linux/unix的文件操作函数.

buffer

  • http://nodejs.cn/api/buffer.html
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
  • buffer可以认为是typedarray的Uint8Array的一种实现, 对应于c语言的uint8_t
  • 不要new, 哈哈, 正合吾意.
  • 用这三个函数: Buffer.from()Buffer.alloc()、和 Buffer.allocUnsafe()

c感悟

  • js完全可以和c一一对应.
  • 之前一个这样的语言是perl, 不小心失败了.

参考

官方文档
  • http://wiki.jikexueyuan.com/project/nodejs/file-system.html
  • http://nodejs.cn/api/buffer.html
  • https://nodejs.org/api/buffer.html

进入html5的领域

  • 有一个原生接口叫file: https://developer.mozilla.org/en-US/docs/Web/API/File
https://electronjs.org/docs/api/file-object
拖拽的文件处理
<div id="holder">
    Drag your file here
  </div>
  
  <script>
    document.addEventListener('drop', function (e) {
      e.preventDefault();
      e.stopPropagation();
      
      for (let f of e.dataTransfer.files) {
        console.log('File(s) you dragged here: ', f.path)
      }
    });
    document.addEventListener('dragover', function (e) {
      e.preventDefault();
      e.stopPropagation();
    });
  </script>
  • 关于file的html5操作, 这个文档讲的很好: https://www.html5rocks.com/en/tutorials/file/dndfiles/

那么目前的问题就是: 如何在electron中操作文件.

  • 菜单操作+dialog操作https://stackoverflow.com/questions/38067298/saving-files-locally-with-electron
    • 菜单: https://electronjs.org/docs/api/menu
    • dialog: https://electronjs.org/docs/api/dialog
    • https://electronjs.org/docs/api/dialog#dialogshowsavedialogbrowserwindow-options-callback
  • 一个指南: https://ourcodeworld.com/articles/read/106/how-to-choose-read-save-delete-or-create-a-file-with-electron-framework
  • electron相关实操参考隔壁的blog

补充一个实例, 如何读取文件尾部的128字节(处理过id3的秒懂)

//文件长度
let filesize
//读取尾部的128字节
fs.stat(sname, initstat)

function initstat(err, data){
    filesize= data.size
    //读取尾部, 128字节, 由于从0编号, 因此最后一个字节是(长度-1)
    const fes=fs.createReadStream(sname, { start: (filesize-128), end: (filesize-1) });	
    fes.on('data', (chunk) => {
        console.log(`Received ${chunk.length} bytes of data.`);
        tailh.value =chunk
        tailb.value =chunk
    });
}				

总结: 明确的说, 尽量别用read[bytes], 那个并不好, 也是官方不推荐的, 关键就是无法控制是否读好了. 是否能读…
官方推荐的做法还是我上面的做法, 在建立这个流的时候可以设置好位置, 然后, 就用on(‘data’)这样的事件来处理.

参考:

  • https://medium.freecodecamp.org/node-js-streams-everything-you-need-to-know-c9141306be93
  • https://nodejs.org/api/url.html#url_url_format_url_options
path包很有用啊
  • 参考: https://nodejs.org/api/path.html#path_path_join_paths
  • 不论是拿目录
  • 扩展名
  • 文件名
  • 都有直接的函数可以用, 相当方便.
  • 需要做文件处理的小伙伴建议阅读以下, 就不必自己写正则了.
  • 特别要指出的是pathname是path串里去除掉域名的后面的那部分例如:
    • url.parse(‘http://stackoverflow.com/questions/17184791’).pathname
    • will give: “/questions/17184791”
  • 用path, 取文件名.
  • https://nodejs.org/api/path.html
  • path是需要引入的: const path = require(‘path’);