在我在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
完全相同(即发送空文件)。
这是我用来发送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);
});
}
答案 0 :(得分:0)
让我们从FileEntry
或Entry
(由相机提供)开始。您必须将该条目转换为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",
}
)
)
)
}