文件下载到本地后内容丢失
============

#### 问题现象

文件内容下载到沙箱后，文件内容始终只有一部分。

#### 背景知识

[文件管理](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-fs)：基础文件操作API，提供基础文件操作能力，包括文件基本管理、文件目录管理、文件信息统计、文件流式读写等常用功能。

[fs.createStreamSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-fs#fscreatestreamsync)：以同步方法基于文件路径创建文件流。需要配合Stream中的close()函数关闭文件流。

#### 问题定位

问题代码如下：

_/\*文件保存到沙箱\*/_
try {
  request.downloadFile(this.context, {
    url: this.downloadUrl,
    filePath: this.filesDir + '/' + this.name
  }).then(async (downloadTask: request.DownloadTask) => {
    downloadTask.on('complete', async () => {
      console.info('download complete');
      let file =
        fs.openSync(this.filesDir + '/' + this.name, fs.OpenMode.READ\_WRITE | fs.OpenMode.CREATE);
      let arrayBuffer = new ArrayBuffer(1024);
      let readLen = fs.readSync(file.fd, arrayBuffer);
      let buf = buffer.from(arrayBuffer, 0, readLen);
      console.info(\`The content of file: ${buf.toString()}\`);
      fs.closeSync(file);
      this.savePath(filePath, this.name, buf)
    })
  }).catch((err: BusinessError) => {
    console.error(\`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}\`);
  });
} catch (error) {
  let err: BusinessError = error as BusinessError;
  console.error(\`Invoke downloadFile failed, code is ${err.code}, message is ${err.message}\`);
}

文件能读取到内容，说明成功下载到沙箱里了。始终只有一部分很可能是文件较长，使用buffer时处理不当，导致只有开头部分的内容被存入了沙箱，因此着重排查这部分代码。

let arrayBuffer = new ArrayBuffer(1024);
let readLen = fs.readSync(file.fd, arrayBuffer);
let buf = buffer.from(arrayBuffer, 0, readLen);

很明显ArrayBuffer被写成了一个固定的值，也没有进行循环操作等处理，因此只有前1024位内容被存入了沙箱。

#### 分析结论

下载时buffer值写成了固定值且无循环读取操作，只有开头的内容被存入沙箱。

#### 修改建议

可以参考以下代码写法，在使用buffer时，将文件流存到一个buffer数组内，最后将这些buffer合并到一起，除了修正了原问题外，在文件较大时也可以避免单个bufSize过大导致的内容丢失等问题。

import fs from '@ohos.file.fs';
import { buffer } from '@kit.ArkTS';
import { BusinessError } from '@ohos.base';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';
  async readLocalFileWithStream() {
    try {
      let buffers: buffer.Buffer\[\] = \[\];
      let inputStream = fs.createStreamSync('文件沙箱路径', 'r+');
      let bufSize = 1024;
      let readSize = 0;
      let buf = new ArrayBuffer(bufSize);

      class Option {
        public offset: number = 0;
        public length: number = bufSize;
      }

      let option = new Option();
      option.offset = readSize;
      let readLen = await inputStream.read(buf, option);
      _// 存入当前结果_
      buffers.push(buffer.from(buf.slice(0, readLen)));
      readSize += readLen;
      while (readLen > 0) {
        option.offset = readSize;
        readLen = await inputStream.read(buf, option);
        buffers.push(buffer.from(buf.slice(0, readLen)));
        readSize += readLen;
      }
      inputStream.closeSync();
      _// 合并内容_
      let finalBuf: ArrayBuffer = buffer.concat(buffers).buffer;
      console.info(\`final ArrayBuffer byteLength is ${finalBuf.byteLength}}\`);
    } catch (error) {
      let err = error as BusinessError;
      console.error(\`readLocalFileWithStream failed, code is ${err.code},message is ${err.message}\`);
    }
  }

  build() {
    RelativeContainer() {
      Text(this.message)
        .fontSize($r('app.float.page\_text\_font\_size'))
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '\_\_container\_\_', align: VerticalAlign.Center },
          middle: { anchor: '\_\_container\_\_', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          this.message = 'Welcome';
        })
    }
    .height('100%')
    .width('100%')
  }
}