diff --git a/src/core/fileuploader/providers/helper.ts b/src/core/fileuploader/providers/helper.ts index f6f9bd334..9c1f6805f 100644 --- a/src/core/fileuploader/providers/helper.ts +++ b/src/core/fileuploader/providers/helper.ts @@ -19,7 +19,6 @@ import { Camera, CameraOptions } from '@ionic-native/camera'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFileProvider, CoreFileProgressEvent } from '@providers/file'; -import { CoreFileHelperProvider } from '@providers/file-helper'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -48,8 +47,7 @@ export class CoreFileUploaderHelperProvider { protected actionSheetCtrl: ActionSheetController, protected uploaderDelegate: CoreFileUploaderDelegate, protected camera: Camera, - protected platform: Platform, - protected fileHelper: CoreFileHelperProvider) { + protected platform: Platform) { this.logger = logger.getInstance('CoreFileUploaderProvider'); } @@ -108,7 +106,7 @@ export class CoreFileUploaderHelperProvider { const filePath = this.textUtils.concatenatePaths(CoreFileProvider.TMPFOLDER, newName); // Write the data into the file. - return this.fileHelper.writeFileDataInFile(file, filePath, (progress: CoreFileProgressEvent) => { + return this.fileProvider.writeFileDataInFile(file, filePath, (progress: CoreFileProgressEvent) => { this.showProgressModal(modal, 'core.fileuploader.readingfileperc', progress); }); }).catch((error) => { @@ -193,9 +191,7 @@ export class CoreFileUploaderHelperProvider { } }); - this.domUtils.showErrorModal(errorMessage); - - return Promise.reject(null); + return Promise.reject(errorMessage); } /** @@ -649,7 +645,7 @@ export class CoreFileUploaderHelperProvider { this.fileProvider.removeExternalFile(path); } - return Promise.reject(null); + return Promise.reject(this.domUtils.createCanceledError()); }); }; diff --git a/src/providers/file-helper.ts b/src/providers/file-helper.ts index ee3a3111d..9a9ab8eaf 100644 --- a/src/providers/file-helper.ts +++ b/src/providers/file-helper.ts @@ -15,16 +15,13 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from './app'; -import { CoreConfigProvider } from './config'; -import { CoreFileProvider, CoreFileProgressFunction } from './file'; +import { CoreFileProvider } from './file'; import { CoreFilepoolProvider } from './filepool'; import { CoreSitesProvider } from './sites'; import { CoreWSProvider } from './ws'; -import { CoreDomUtilsProvider } from './utils/dom'; import { CoreUrlUtils } from './utils/url'; import { CoreUtilsProvider } from './utils/utils'; import { CoreConstants } from '@core/constants'; -import { FileEntry } from '@ionic-native/file'; import { makeSingleton } from '@singletons/core.singletons'; /** @@ -33,23 +30,13 @@ import { makeSingleton } from '@singletons/core.singletons'; @Injectable() export class CoreFileHelperProvider { - // Variables for reading files in chunks. - protected MAX_CHUNK_SIZE_NAME = 'file_max_chunk_size'; - protected READ_CHUNK_ATTEMPT_NAME = 'file_read_chunk_attempt'; - protected maxChunkSize = -1; - constructor(protected fileProvider: CoreFileProvider, protected filepoolProvider: CoreFilepoolProvider, protected sitesProvider: CoreSitesProvider, protected appProvider: CoreAppProvider, protected translate: TranslateService, protected utils: CoreUtilsProvider, - protected wsProvider: CoreWSProvider, - protected configProvider: CoreConfigProvider, - protected domUtils: CoreDomUtilsProvider) { - - this.initMaxChunkSize(); - } + protected wsProvider: CoreWSProvider) { } /** * Convenience function to open a file, downloading it if needed. @@ -258,34 +245,6 @@ export class CoreFileHelperProvider { return file.timemodified || 0; } - /** - * Initialize the max chunk size. - * - * @return Promise resolved when done. - */ - protected async initMaxChunkSize(): Promise { - const sizes = await Promise.all([ - await this.configProvider.get(this.READ_CHUNK_ATTEMPT_NAME, -1), // Check if there is any attempt pending. - await this.configProvider.get(this.MAX_CHUNK_SIZE_NAME, -1), // Retrieve current max chunk size from DB. - ]); - - const attemptSize = sizes[0]; - const maxChunkSize = sizes[1]; - - if (attemptSize != -1 && (maxChunkSize == -1 || attemptSize < maxChunkSize)) { - // Store the attempt's size as the max size. - this.storeMaxChunkSize(attemptSize); - } else { - // No attempt or the max size is already lower. Keep current max size. - this.maxChunkSize = maxChunkSize; - } - - if (attemptSize != -1) { - // Clean pending attempt. - await this.configProvider.delete(this.READ_CHUNK_ATTEMPT_NAME); - } - } - /** * Check if a state is downloaded or outdated. * @@ -380,99 +339,6 @@ export class CoreFileHelperProvider { throw new Error('Couldn\'t determine file size: ' + file.fileurl); } - - /** - * Save max chunk size. - * - * @param size Size to store. - * @return Promise resolved when done. - */ - protected async storeMaxChunkSize(size: number): Promise { - this.maxChunkSize = size; - - await this.configProvider.set(this.MAX_CHUNK_SIZE_NAME, size); - } - - /** - * Write some file data into a filesystem file. - * It's done in chunks to prevent crashing the app for big files. - * - * @param file The data to write. - * @param path Path where to store the data. - * @param onProgress Function to call on progress. - * @param offset Offset where to start reading from. - * @param append Whether to append the data to the end of the file. - * @return Promise resolved when done. - */ - async writeFileDataInFile(file: Blob, path: string, onProgress?: CoreFileProgressFunction, offset: number = 0, - append?: boolean): Promise { - - offset = offset || 0; - - // Get the chunk to read and write. - const readWholeFile = offset === 0 && CoreFileProvider.CHUNK_SIZE >= file.size; - const chunk = readWholeFile ? file : file.slice(offset, Math.min(offset + CoreFileProvider.CHUNK_SIZE, file.size)); - - try { - const fileEntry = await this.fileProvider.writeFileDataInFileChunk(chunk, path, append); - - offset += CoreFileProvider.CHUNK_SIZE; - - onProgress && onProgress({ - lengthComputable: true, - loaded: offset, - total: file.size - }); - - if (offset >= file.size) { - // Done, stop. - return fileEntry; - } - - // Read the next chunk. - return this.writeFileDataInFile(file, path, onProgress, offset, true); - } catch (error) { - if (readWholeFile || !error || error.name != 'NotReadableError') { - return Promise.reject(error); - } - - // Permission error when reading file in chunks. This usually happens with Google Drive files. - // Try to read the whole file at once. - return this.writeBigFileDataInFile(file, path, onProgress); - } - } - - /** - * Writes a big file data into a filesystem file without using chunks. - * The app can crash when doing this with big files, so this function will try to control the max size that works - * and warn the user if he's trying to upload a file that is too big. - * - * @param file The data to write. - * @param path Path where to store the data. - * @param onProgress Function to call on progress. - * @return Promise resolved with the file. - */ - protected async writeBigFileDataInFile(file: Blob, path: string, onProgress?: CoreFileProgressFunction): Promise { - if (this.maxChunkSize != -1 && file.size >= this.maxChunkSize) { - // The file size is bigger than the max allowed size. Ask the user to confirm. - await this.domUtils.showConfirm(this.translate.instant('core.confirmreadfiletoobig')); - } - - // Store the "attempt". - await this.configProvider.set(this.READ_CHUNK_ATTEMPT_NAME, file.size); - - // Write the whole file. - const fileEntry = await this.fileProvider.writeFileDataInFileChunk(file, path, false); - - // Success, remove the attempt and increase the max chunk size if needed. - await this.configProvider.delete(this.READ_CHUNK_ATTEMPT_NAME); - - if (file.size > this.maxChunkSize) { - await this.storeMaxChunkSize(file.size + 1); - } - - return fileEntry; - } } export class CoreFileHelper extends makeSingleton(CoreFileHelperProvider) {} diff --git a/src/providers/file.ts b/src/providers/file.ts index 06137afa9..a5150bc55 100644 --- a/src/providers/file.ts +++ b/src/providers/file.ts @@ -65,7 +65,7 @@ export class CoreFileProvider { static SITESFOLDER = 'sites'; static TMPFOLDER = 'tmp'; - static CHUNK_SIZE = 10485760; // 10 MB. + static CHUNK_SIZE = 1048576; // 1 MB. Same chunk size as Ionic Native. protected logger; protected initialized = false; @@ -639,6 +639,7 @@ export class CoreFileProvider { /** * Write some file data into a filesystem file. * It's done in chunks to prevent crashing the app for big files. + * Please notice Ionic Native writeFile function already splits by chunks, but it doesn't have an onProgress function. * * @param file The data to write. * @param path Path where to store the data. @@ -646,19 +647,17 @@ export class CoreFileProvider { * @param offset Offset where to start reading from. * @param append Whether to append the data to the end of the file. * @return Promise resolved when done. - * @deprecated since 3.8.3. Please use CoreFileHelperProvider.writeFileDataInFile instead. */ async writeFileDataInFile(file: Blob, path: string, onProgress?: CoreFileProgressFunction, offset: number = 0, append?: boolean): Promise { offset = offset || 0; - // Get the chunk to read and write. - const readWholeFile = offset === 0 && CoreFileProvider.CHUNK_SIZE >= file.size; - const chunk = readWholeFile ? file : file.slice(offset, Math.min(offset + CoreFileProvider.CHUNK_SIZE, file.size)); - try { - const fileEntry = await this.writeFileDataInFileChunk(chunk, path, append); + // Get the chunk to write. + const chunk = file.slice(offset, Math.min(offset + CoreFileProvider.CHUNK_SIZE, file.size)); + + const fileEntry = await this.writeFile(path, chunk, append); offset += CoreFileProvider.CHUNK_SIZE; @@ -676,32 +675,15 @@ export class CoreFileProvider { // Read the next chunk. return this.writeFileDataInFile(file, path, onProgress, offset, true); } catch (error) { - if (readWholeFile || !error || error.name != 'NotReadableError') { - return Promise.reject(error); + if (error && error.target && error.target.error) { + // Error returned by the writer, get the "real" error. + error = error.target.error; } - // Permission error when reading file in chunks. This usually happens with Google Drive files. - // Try to read the whole file at once. - return this.writeFileDataInFileChunk(file, path, false); + throw error; } } - /** - * Write a chunk of data into a file. - * - * @param chunkData The chunk of data. - * @param path Path where to store the data. - * @param append Whether to append the data to the end of the file. - * @return Promise resolved when done. - */ - writeFileDataInFileChunk(chunkData: Blob, path: string, append?: boolean): Promise { - // Read the chunk data. - return this.readFileData(chunkData, CoreFileProvider.FORMATARRAYBUFFER).then((fileData) => { - // Write the data in the file. - return this.writeFile(path, fileData, append); - }); - } - /** * Gets a file that might be outside the app's folder. * diff --git a/src/providers/ws.ts b/src/providers/ws.ts index 57c91985a..996d3e61f 100644 --- a/src/providers/ws.ts +++ b/src/providers/ws.ts @@ -901,7 +901,7 @@ export class CoreWSProvider { return transfer.upload(filePath, uploadUrl, options, true).then((success) => { const data = this.textUtils.parseJSON(success.response, null, - this.logger.error.bind(this.logger, 'Error parsing response from upload')); + this.logger.error.bind(this.logger, 'Error parsing response from upload', success.response)); if (data === null) { return Promise.reject(this.translate.instant('core.errorinvalidresponse')); }