Загрузка фрагментированного файла с использованием Ionic4 и Angular HttpClient завершается неудачно после некоторых успешных загрузок с net :: ERR_FILE_NOT_FOUND - PullRequest
0 голосов
/ 29 марта 2020

Я пытаюсь загрузить большой файл (500+ Мб, но может быть даже больше) на наш php сервер, используя приложение, написанное на Ionic4 + Angular + Cordova, на эмуляторе с Android 10. Я настроил систему для загрузки файла кусками. Он читает файл, выбранный пользователем, используя THIS PLUGIN , порция за порцию (5Mb на порцию). Затем он отправляет его на наш сервер, выполняя запрос POST с типом содержимого multipart / form-data. Файл отправляется на сервер, сервер сохраняет его, говорит «ОК», затем приложение отправляет следующий фрагмент. Все отлично работает, для первых 25/29 кусков. Затем запрос POST завершается с ошибкой

POST http://192.168.1.2/work/path/to/webservices/uploadChunks.php net::ERR_FILE_NOT_FOUND

enter image description here

Я попытался:

  • , начиная с другой точки в файле вместо байта 0 - получена та же ошибка
  • чтение фрагмента файла по чанку, без выполнения любого запроса POST - может циклически обрабатывать весь файл размером 500 МБ
  • чтение фрагмента файла по чанку и создание POST запросы, но не отправка чанков с ними - мог выполнить каждый отдельный вызов без какой-либо ошибки, через конец файла
  • , читая чанк файла по чанку и отправляя их в ДРУГОЙ веб-сервис - получил ту же ошибку
  • чтение фрагмента файла по фрагменту и выполнение POST-запроса к другому веб-сервису с приложением типа контента / json и помещение объекта formData в тело запроса (не уверен, что это допустимый тест, хотя) - может быть выполнено каждый отдельный вызов без ошибок, до конца файла

Проверка снимков памяти, сделанных в инспекторе 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);
    }
);

Я не могу понять, что происходит не так. Может кто-нибудь помочь мне отладить это, или знает, что может происходить?

Большое спасибо.

1 Ответ

0 голосов
/ 08 апреля 2020

Я до сих пор не знаю, что вызвало эту проблему, но я решил использовать запрос PUT вместо запроса POST, отправив необработанный фрагмент и поместив дополнительные данные в пользовательские заголовки (что-то вроде «X-nr-chunk» или « X-TOT-кусок "). Загрузка завершена нормально без сообщения об ошибке.

Я также использовал плагин cordova-advanced-http , но я не думаю, что здесь что-то изменилось, так как он не работал с POST запрос, как и другой метод (httpClient).

Это было проверено только на android, но не на iOS. Я сообщу, если есть какие-либо проблемы. Пока я считаю, что это решено, но если вы знаете, что, возможно, вызвало эту проблему, пожалуйста, поделитесь своими мыслями.

Спасибо всем.

...