已发送Blob结果为空

时间:2015-06-04 14:21:34

标签: android ajax cordova

在我在Android 4.0上测试的Cordova / Phonegap应用中,我需要通过ajax将文件作为multipart/form-data发送到服务器。

我在ArrayBuffer中有文件内容,我将其放在FormData中,首先从中创建Blob

问题是发送的文件似乎是空的。

这是一个控制台会话(在Android平台上通过weinre执行),您可以在其中看到:

  • 文件内容已加载到mybuf
  • Blob创建mybuf(其大小非零)
  • 创建了一个FormData对象(fd

(我正在使用WebKitBlobBuilder,因为Blob构造函数在此平台上引发了TypeError

❯ mybuf
  ▼ ArrayBuffer
      byteLength: 23673
    ▶ __proto__: ArrayBuffer
❯ var bb = new WebKitBlobBuilder()
  undefined
❯ bb.append(mybuf)
  undefined
❯ myblob = bb.getBlob("image/jpeg")
  ▼ Blob
      size: 23673
      type: "image/jpeg"
    ▶ __proto__: Blob
❯ fd = new FormData()
  ▶ FormData
❯ fd.append("pics[]", myblob, "1433412118197.jpg")
  undefined

当我执行将fd对象作为数据传递的ajax请求时,我看到该文件实际已发送(我在请求中看到name="pics[]"),但其内容为空。

这是请求的回应:

POST /test/post/ HTTP/1.1
Host: 192.168.1.88:50000
Connection: keep-alive
Content-Length: 212
Origin: file://
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywFAAx1aqKBZ6uuOf
Accept: */*
User-Agent: Mozilla/5.0 (Linux; U; Android 4.0.4; it-it; M-MP706I Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30
Accept-Encoding: gzip,deflate
Accept-Language: it-IT, en-US
Accept-Charset: utf-8, iso-8859-1, utf-16, *;q=0.7

------WebKitFormBoundarywFAAx1aqKBZ6uuOf
Content-Disposition: form-data; name="pics[]"; filename="Blobd380f922a76d4b03908c426487d2fa68"
Content-Type: image/jpeg


------WebKitFormBoundarywFAAx1aqKBZ6uuOf--

我还尝试了什么

我还试图从Uint8Array构建一个ArrayBuffer,并用几种方式构建Blob,但没有一个工作。 Uint8Array似乎没问题:

❯ var myu8buf = new Uint8Array(mybuf)
  undefined
❯ myu8buf
  ▼ Uint8Array
      0: 255
      1: 216
      2: 255
      3: 225
      4: 1
      5: 20
      6: 69
      7: 120
      ...

然后,根据我追加到BlobBuilder的内容,Blob大小会有所不同:

  • bb.append([myu8buf]) - Blob size:19
  • bb.append(myu8buf) - Blob size:19
  • bb.append([myu8buf.buffer]) - Blob size:20
  • bb.append(myu8buf.buffer) - Blob size:23673

所以最后一个似乎是正确的,但如果发送它的行为与原始的ArrayBuffer完全相同(即发送空文件)。

文件读取和Ajax函数

这是我用来发送FormData(用jQuery)的函数:

function sendform(fd) {
    $.ajax({
      url: 'http://192.168.1.88:50000/test/post/',
      data: fd,
      processData: false,
      contentType: false,
      type: 'POST',
      success: function(d){
        console.log("data received:");
        console.log(d);
      },
      error: function(d) {
        console.log("ajax error!");
      }
    });
}

这是在ArrayBuffer中读取文件内容的功能:

function file2buf(filename, callback) {

  window.resolveLocalFileSystemURL(filename, function(fileEntry) {

    return fileEntry.file(function(file) { // success (fileEntry.file)

      var reader = new FileReader();

      reader.onloadend = function(fileReadResult) {

        console.log("file type: "+file.type);
        console.log("file name: "+file.name);

        callback(fileReadResult.target.result);

      };

      return reader.readAsArrayBuffer(file);

    }, function(e) { // fail (fileEntry.file)
      console.log("ERR in fileEntry.file");
      console.log(e);
    });
  }, function(e) { // fail (resolveLocalFileSystemURL)
    console.log("ERR in resolveLocalFileSystemURL");
    console.log(e);
  });

}

1 个答案:

答案 0 :(得分:0)

让我们从FileEntryEntry(由相机提供)开始。您必须将该条目转换为Blob

这是一种可以帮助您的方法。请注意,这会将对象加载到内存中,但由于无法从文件系统进行流传输,因此我找不到解决该问题的方法。

  private async fileEntryToBlob(entry: Entry): Promise<Blob> {

    if (!entry.isFile) {
      throw Error(`expected ${entry} to be a file`);
    }
    const fileEntry = (entry as FileEntry);
    return new Promise<Blob>(
      (resolve, reject) => {

        fileEntry.file(
          (file) => {
            const reader = new FileReader();
            reader.onloadend = function (evt) {
              resolve(new Blob([evt.target.result]));
            }
            reader.readAsArrayBuffer(file);
          },
          (err) => { reject(err); },
        );

      });
  }

可以使用承诺将blob附加到FormData中,如此处所示。

  /**
   * Uploads an artifact to the backend and returns a promise for
   * the upload result.  Progress cannot be tracked with this one.
   *
   * @param fileEntry file entry to upload
   */
  public async uploadFile(fileEntry: Entry): Promise<IUploadResult> {
    const url = ....

    const blob: Blob = await this.fileEntryToBlob(fileEntry);
    const formData = new FormData();
    formData.append("uploadFile", blob, fileEntry.name);
    return this.http
      .post<IUploadResult>(
      url,
      formData
    )
      .toPromise();
  }

这是另一个可观察的流。

 /**
   * Uploads an artifact to the backend and returns an observable of
   * HttpEvents.  That way progress can be tracked.
   *
   * @param fileEntry file entry to upload
   */
  public uploadFile$(fileEntry: Entry): Observable<HttpEvent<IUploadResult>> {
    const url = ...

    const formData$ = defer(async () => {
      const blob: Blob = await this.fileEntryToBlob(fileEntry);
      const formData = new FormData();
      formData.append("uploadFile", blob, fileEntry.name);
      return formData;
    });

    return formData$.pipe(
      mergeMap(formData =>
        this.http
          .post<IUploadResult>(
          url,
          formData,
          {
            reportProgress: true,
            observe: "events",
          }
        )
      )
    )
  }