使用 net::ERR_FILE_NOT_FOUND 成功上传后,使用 Ionic4 和 Angular HttpClient 上传分块文件失败
Uploading chunked file using Ionic4 and Angular HttpClient fails after some successful uploads with net::ERR_FILE_NOT_FOUND
我正在尝试上传一个大文件(500+Mb,但可能更大)到我们的 php 服务器,使用一个用 Ionic4+Angular+Cordova 编写的应用程序,在一个Android 10 的模拟器。
我设置了一个系统来分块上传文件。
它使用 THIS PLUGIN 逐块读取用户选择的文件(每块 5Mb)。
然后它继续将其发送到我们的服务器,执行内容类型 multipart/form-data 的 POST 请求。
该文件转到服务器,服务器保存它,说 "OK",然后应用程序继续发送以下块。
对于前 25/29 个块,一切正常。
然后,POST 请求失败
POST http://192.168.1.2/work/path/to/webservices/uploadChunks.php net::ERR_FILE_NOT_FOUND
我试过了:
- 从文件中的另一点而不是字节 0 开始 - 出现相同的错误
- 逐块读取文件,无需发出任何 POST 请求 - 可以循环整个 500Mb 文件
- 逐块读取文件并发出 POST 请求,但不发送块 - 可以执行每个调用而不会出现任何错误,直到文件末尾
- 逐块读取文件并将它们发送到另一个网络服务 - 出现相同的错误
- 逐块读取文件并向另一个网络服务执行 POST 请求,内容类型为 application/json 并将 formData 对象放入请求正文(不确定这是有效测试tho) - 可以在文件末尾无错误地执行每个调用
检查 chrome 检查器在不同块上传期间拍摄的内存快照没有显示任何内存泄漏迹象。
这个案例是在一台相当旧的设备上测试的,同样的程序导致应用程序退出,没有发出任何错误信号(显然 logcat 中也没有)。
这是用于分块和发送文件的代码片段:
const generatedName = 'some_name_for_file';
// Path obtained from fileChooser plugin
let path_to_file = 'content://com.android.externalstorage.documents/document/primary%3ADownload%2Ffilename.avi'
const min_chunk_size = (5 * 1024 * 1024);
// Converting path to file:// path
this.filePath.resolveNativePath(path_to_file).then((resolvedPath) => {
return this.fileAPI.resolveLocalFilesystemUrl(resolvedPath);
}, (error) => {
console.log('ERROR FILEPATH');
console.log(error);
return Promise.reject('Can not access file.<br>Code : F - ' + error);
}).then(
(entry) => {
path_to_file = entry.toURL();
console.log(path_to_file);
(entry as FileEntry).file((file) => {
//Getting back to the zone
this.ngZone.run(() => {
// Re-computing chunk size to be sure we do not get more than 10k chunks (very remote case)
let file_chunk_size = file.size / 10000;
if (file_chunk_size < min_chunk_size) {
file_chunk_size = min_chunk_size;
}
//Total number of chunks
const tot_chunk = Math.ceil(file.size / file_chunk_size);
const reader = new FileReader();
let retry_count = 0; //Counter to check on retries
const readFile = (nr_part: number, part_start: number, length: number) => {
// Computing end of chunk
const part_end = Math.min(part_start + length, file.size);
// Slicing file to get desired chunk
const blob = file.slice(part_start, part_end);
reader.onload = (event: any) => {
if (event.target.readyState === FileReader.DONE) {
let formData = new FormData();
//Creating blob
let fileBlob = new Blob([reader.result], {
type: file.type
});
formData.append('file', fileBlob, generatedName || file.name);
formData.append('tot_chunk', tot_chunk.toString());
formData.append('nr_chunk', nr_part.toString());
// UPLOAD
const sub = this.http.post('http://192.168.1.2/path/to/webservice/uploadChunk.php', formData).subscribe({
next: (response: any) => {
console.log('UPLOAD completed');
console.log(response);
retry_count = 0;
if (response && response.status === 'OK') {
//Emptying form and blob to be sure memory is clean
formData = null;
fileBlob = null;
// Checking if this was the last chunk
if (part_end >= file.size) {
// END
callback({
status: 'OK'
});
} else {
// Go to next chunk
readFile(nr_part + 1, part_end, length);
}
//Clearing post call subscription
sub.unsubscribe();
} else {
//There was an error server-side
callback(response);
}
},
error: (err) => {
console.log('POST CALL ERROR');
console.log(err);
if (retry_count < 5) {
setTimeout(() => {
retry_count++;
console.log('RETRY (' + (retry_count + 1) + ')');
readFile(nr_part, part_start, length);
}, 1000);
} else {
console.log('STOP RETRYING');
callback({status:'ERROR'});
}
}
});
}
};
//If for some reason the start point is after the end point, we exit with success...
if (part_start < part_end) {
reader.readAsArrayBuffer(blob);
} else {
callback({
status: 'OK'
});
}
};
//Start reading chunks
readFile(1, 0, file_chunk_size);
});
}, (error) => {
console.log('DEBUG - ERROR 3 ');
console.log(error);
callback({
status: 'ERROR',
code: error.code,
message: 'Can not read file<br>(Code: 3-' + error.code + ')'
});
});
}, (error) => {
console.log('ERROR 3');
console.log(error);
return Promise.reject('Can not access file.<br>Code : 3 - ' + error);
}
);
我不知道出了什么问题。谁能帮我调试这个,或者知道会发生什么?
非常感谢。
我仍然不知道是什么导致了这个问题,但我解决了使用 PUT 请求而不是 POST 请求,发送原始数据块,并将额外数据放入自定义 headers(某事例如 "X-nr-chunk" 或 "X-tot-chunk")。上传正常,没有错误消息。
我也使用了 cordova-advanced-http 插件,但我认为它在这里没有什么不同,因为它不适用于 POST 请求,就像其他方法 (httpClient) 一样。
目前仅在 android 上测试过,未在 iOS 上测试过。如果有任何问题,我会报告。现在我认为这已经解决了,但是如果您知道可能导致此问题的原因,请分享您的想法。
谢谢大家
我正在尝试上传一个大文件(500+Mb,但可能更大)到我们的 php 服务器,使用一个用 Ionic4+Angular+Cordova 编写的应用程序,在一个Android 10 的模拟器。 我设置了一个系统来分块上传文件。 它使用 THIS PLUGIN 逐块读取用户选择的文件(每块 5Mb)。 然后它继续将其发送到我们的服务器,执行内容类型 multipart/form-data 的 POST 请求。 该文件转到服务器,服务器保存它,说 "OK",然后应用程序继续发送以下块。 对于前 25/29 个块,一切正常。 然后,POST 请求失败
POST http://192.168.1.2/work/path/to/webservices/uploadChunks.php net::ERR_FILE_NOT_FOUND
我试过了:
- 从文件中的另一点而不是字节 0 开始 - 出现相同的错误
- 逐块读取文件,无需发出任何 POST 请求 - 可以循环整个 500Mb 文件
- 逐块读取文件并发出 POST 请求,但不发送块 - 可以执行每个调用而不会出现任何错误,直到文件末尾
- 逐块读取文件并将它们发送到另一个网络服务 - 出现相同的错误
- 逐块读取文件并向另一个网络服务执行 POST 请求,内容类型为 application/json 并将 formData 对象放入请求正文(不确定这是有效测试tho) - 可以在文件末尾无错误地执行每个调用
检查 chrome 检查器在不同块上传期间拍摄的内存快照没有显示任何内存泄漏迹象。
这个案例是在一台相当旧的设备上测试的,同样的程序导致应用程序退出,没有发出任何错误信号(显然 logcat 中也没有)。
这是用于分块和发送文件的代码片段:
const generatedName = 'some_name_for_file';
// Path obtained from fileChooser plugin
let path_to_file = 'content://com.android.externalstorage.documents/document/primary%3ADownload%2Ffilename.avi'
const min_chunk_size = (5 * 1024 * 1024);
// Converting path to file:// path
this.filePath.resolveNativePath(path_to_file).then((resolvedPath) => {
return this.fileAPI.resolveLocalFilesystemUrl(resolvedPath);
}, (error) => {
console.log('ERROR FILEPATH');
console.log(error);
return Promise.reject('Can not access file.<br>Code : F - ' + error);
}).then(
(entry) => {
path_to_file = entry.toURL();
console.log(path_to_file);
(entry as FileEntry).file((file) => {
//Getting back to the zone
this.ngZone.run(() => {
// Re-computing chunk size to be sure we do not get more than 10k chunks (very remote case)
let file_chunk_size = file.size / 10000;
if (file_chunk_size < min_chunk_size) {
file_chunk_size = min_chunk_size;
}
//Total number of chunks
const tot_chunk = Math.ceil(file.size / file_chunk_size);
const reader = new FileReader();
let retry_count = 0; //Counter to check on retries
const readFile = (nr_part: number, part_start: number, length: number) => {
// Computing end of chunk
const part_end = Math.min(part_start + length, file.size);
// Slicing file to get desired chunk
const blob = file.slice(part_start, part_end);
reader.onload = (event: any) => {
if (event.target.readyState === FileReader.DONE) {
let formData = new FormData();
//Creating blob
let fileBlob = new Blob([reader.result], {
type: file.type
});
formData.append('file', fileBlob, generatedName || file.name);
formData.append('tot_chunk', tot_chunk.toString());
formData.append('nr_chunk', nr_part.toString());
// UPLOAD
const sub = this.http.post('http://192.168.1.2/path/to/webservice/uploadChunk.php', formData).subscribe({
next: (response: any) => {
console.log('UPLOAD completed');
console.log(response);
retry_count = 0;
if (response && response.status === 'OK') {
//Emptying form and blob to be sure memory is clean
formData = null;
fileBlob = null;
// Checking if this was the last chunk
if (part_end >= file.size) {
// END
callback({
status: 'OK'
});
} else {
// Go to next chunk
readFile(nr_part + 1, part_end, length);
}
//Clearing post call subscription
sub.unsubscribe();
} else {
//There was an error server-side
callback(response);
}
},
error: (err) => {
console.log('POST CALL ERROR');
console.log(err);
if (retry_count < 5) {
setTimeout(() => {
retry_count++;
console.log('RETRY (' + (retry_count + 1) + ')');
readFile(nr_part, part_start, length);
}, 1000);
} else {
console.log('STOP RETRYING');
callback({status:'ERROR'});
}
}
});
}
};
//If for some reason the start point is after the end point, we exit with success...
if (part_start < part_end) {
reader.readAsArrayBuffer(blob);
} else {
callback({
status: 'OK'
});
}
};
//Start reading chunks
readFile(1, 0, file_chunk_size);
});
}, (error) => {
console.log('DEBUG - ERROR 3 ');
console.log(error);
callback({
status: 'ERROR',
code: error.code,
message: 'Can not read file<br>(Code: 3-' + error.code + ')'
});
});
}, (error) => {
console.log('ERROR 3');
console.log(error);
return Promise.reject('Can not access file.<br>Code : 3 - ' + error);
}
);
我不知道出了什么问题。谁能帮我调试这个,或者知道会发生什么?
非常感谢。
我仍然不知道是什么导致了这个问题,但我解决了使用 PUT 请求而不是 POST 请求,发送原始数据块,并将额外数据放入自定义 headers(某事例如 "X-nr-chunk" 或 "X-tot-chunk")。上传正常,没有错误消息。
我也使用了 cordova-advanced-http 插件,但我认为它在这里没有什么不同,因为它不适用于 POST 请求,就像其他方法 (httpClient) 一样。
目前仅在 android 上测试过,未在 iOS 上测试过。如果有任何问题,我会报告。现在我认为这已经解决了,但是如果您知道可能导致此问题的原因,请分享您的想法。
谢谢大家