将字节数组输出转换为 Blob 会损坏文件

问题描述 投票:0回答:3

我正在使用 Office Javascript API 来使用 Angular 编写 Word 插件。

我想通过API检索Word文档,然后将其转换为文件并通过POST上传到服务器。

我使用的代码与 Microsoft 为此用例提供的文档代码几乎相同:https://dev.office.com/reference/add-ins/shared/document.getfileasync#example---get-办公室文档开放 xml 压缩格式

服务器端点要求通过多部分表单发布上传,因此我在创建 $http 调用时创建一个 FormData 对象,在该对象上附加文件(一个 blob)以及一些元数据。

文件正在传输到服务器,但当我打开它时,它已损坏,无法再用Word打开。

根据文档,Office.context.document.getFileAsync 函数返回一个字节数组。但是,生成的 fileContent 变量是一个字符串。当我 console.log 这个字符串时,它似乎是压缩数据,就像它应该的那样。

我的猜测是,在将字符串转换为 Blob 之前,我需要进行一些预处理。但是哪个预处理呢?通过atob进行Base64编码似乎没有做任何事情。

let sendFile = (fileContent) => {

  let blob = new Blob([fileContent], {
      type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    }),
    fd = new FormData();

  blob.lastModifiedDate = new Date();

  fd.append('file', blob, 'uploaded_file_test403.docx');
  fd.append('case_id', caseIdReducer.data());

  $http.post('/file/create', fd, {
      transformRequest: angular.identity,
      headers: {
        'Content-Type': undefined
      }
    })
    .success(() => {

      console.log('upload succeeded');

    })
    .error(() => {
      console.log('upload failed');
    });

};


function onGotAllSlices(docdataSlices) {

  let docdata = [];

  for (let i = 0; i < docdataSlices.length; i++) {
    docdata = docdata.concat(docdataSlices[i]);
  }

  let fileContent = new String();

  for (let j = 0; j < docdata.length; j++) {
    fileContent += String.fromCharCode(docdata[j]);
  }

  // Now all the file content is stored in 'fileContent' variable,
  // you can do something with it, such as print, fax...

  sendFile(fileContent);

}

function getSliceAsync(file, nextSlice, sliceCount, gotAllSlices, docdataSlices, slicesReceived) {
  file.getSliceAsync(nextSlice, (sliceResult) => {

    if (sliceResult.status === 'succeeded') {
      if (!gotAllSlices) { // Failed to get all slices, no need to continue.
        return;
      }

      // Got one slice, store it in a temporary array.
      // (Or you can do something else, such as
      // send it to a third-party server.)
      docdataSlices[sliceResult.value.index] = sliceResult.value.data;
      if (++slicesReceived === sliceCount) {
        // All slices have been received.
        file.closeAsync();

        onGotAllSlices(docdataSlices);

      } else {
        getSliceAsync(file, ++nextSlice, sliceCount, gotAllSlices, docdataSlices, slicesReceived);
      }
    } else {

      gotAllSlices = false;
      file.closeAsync();
      console.log(`getSliceAsync Error: ${sliceResult.error.message}`);
    }
  });
}

// User clicks button to start document retrieval from Word and uploading to server process
ctrl.handleClick = () => {

  Office.context.document.getFileAsync(Office.FileType.Compressed, {
      sliceSize: 65536 /*64 KB*/
    },
    (result) => {
      if (result.status === 'succeeded') {

        // If the getFileAsync call succeeded, then
        // result.value will return a valid File Object.
        let myFile = result.value,
          sliceCount = myFile.sliceCount,
          slicesReceived = 0,
          gotAllSlices = true,
          docdataSlices = [];

        // Get the file slices.
        getSliceAsync(myFile, 0, sliceCount, gotAllSlices, docdataSlices, slicesReceived);

      } else {

        console.log(`Error: ${result.error.message}`);

      }
    }
  );
};
javascript angularjs ms-word ms-office
3个回答
11
投票

我最终用 fileContent 字符串做到了这一点:

let bytes = new Uint8Array(fileContent.length);

for (let i = 0; i < bytes.length; i++) {
    bytes[i] = fileContent.charCodeAt(i);
}

然后我继续使用这些字节构建 Blob:

let blob = new Blob([bytes], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });

如果我随后通过 POST 请求发送此文件,该文件不会被破坏并且可以通过 Word 正确打开。

我仍然觉得这可以通过更少的麻烦/更少的步骤来实现。如果有人有更好的解决方案,我很有兴趣学习。


1
投票

谢谢您的回答,

Uint8Array
就是解决方案。只需一点点改进,即可避免创建字符串:

let bytes = new Uint8Array(docdata.length);
for (var i = 0; i < docdata.length; i++) {
    bytes[i] = docdata[i];
}

0
投票

噗!获取 File 实例而不使用 FileReader api 有什么问题?微软加油!

您应该获取字节数组并将其放入 blob 构造函数中,在 javascript 中将二进制 blob 转换为字符串是一个坏主意,可能会导致“超出范围”错误或不正确的编码

跟着这个做一些事情

var byteArray = new Uint8Array(3)
byteArray[0] = 97
byteArray[1] = 98
byteArray[2] = 99
new Blob([byteArray])

如果块是类型化数组的实例或blob/文件的实例。在这种情况下你可以这样做:

blob = new Blob([blob, chunk])

请...不要对其进行 Base64 编码(约大 3 倍 + 速度更慢)

© www.soinside.com 2019 - 2024. All rights reserved.