MOBILE-3270 core: Revert some changes and improve error handling
parent
9057b62333
commit
e80fa8dd18
|
@ -19,7 +19,6 @@ import { Camera, CameraOptions } from '@ionic-native/camera';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreFileProvider, CoreFileProgressEvent } from '@providers/file';
|
import { CoreFileProvider, CoreFileProgressEvent } from '@providers/file';
|
||||||
import { CoreFileHelperProvider } from '@providers/file-helper';
|
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
@ -48,8 +47,7 @@ export class CoreFileUploaderHelperProvider {
|
||||||
protected actionSheetCtrl: ActionSheetController,
|
protected actionSheetCtrl: ActionSheetController,
|
||||||
protected uploaderDelegate: CoreFileUploaderDelegate,
|
protected uploaderDelegate: CoreFileUploaderDelegate,
|
||||||
protected camera: Camera,
|
protected camera: Camera,
|
||||||
protected platform: Platform,
|
protected platform: Platform) {
|
||||||
protected fileHelper: CoreFileHelperProvider) {
|
|
||||||
this.logger = logger.getInstance('CoreFileUploaderProvider');
|
this.logger = logger.getInstance('CoreFileUploaderProvider');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +106,7 @@ export class CoreFileUploaderHelperProvider {
|
||||||
const filePath = this.textUtils.concatenatePaths(CoreFileProvider.TMPFOLDER, newName);
|
const filePath = this.textUtils.concatenatePaths(CoreFileProvider.TMPFOLDER, newName);
|
||||||
|
|
||||||
// Write the data into the file.
|
// 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);
|
this.showProgressModal(modal, 'core.fileuploader.readingfileperc', progress);
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -193,9 +191,7 @@ export class CoreFileUploaderHelperProvider {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.domUtils.showErrorModal(errorMessage);
|
return Promise.reject(errorMessage);
|
||||||
|
|
||||||
return Promise.reject(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -649,7 +645,7 @@ export class CoreFileUploaderHelperProvider {
|
||||||
this.fileProvider.removeExternalFile(path);
|
this.fileProvider.removeExternalFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(null);
|
return Promise.reject(this.domUtils.createCanceledError());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,16 +15,13 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreAppProvider } from './app';
|
import { CoreAppProvider } from './app';
|
||||||
import { CoreConfigProvider } from './config';
|
import { CoreFileProvider } from './file';
|
||||||
import { CoreFileProvider, CoreFileProgressFunction } from './file';
|
|
||||||
import { CoreFilepoolProvider } from './filepool';
|
import { CoreFilepoolProvider } from './filepool';
|
||||||
import { CoreSitesProvider } from './sites';
|
import { CoreSitesProvider } from './sites';
|
||||||
import { CoreWSProvider } from './ws';
|
import { CoreWSProvider } from './ws';
|
||||||
import { CoreDomUtilsProvider } from './utils/dom';
|
|
||||||
import { CoreUrlUtils } from './utils/url';
|
import { CoreUrlUtils } from './utils/url';
|
||||||
import { CoreUtilsProvider } from './utils/utils';
|
import { CoreUtilsProvider } from './utils/utils';
|
||||||
import { CoreConstants } from '@core/constants';
|
import { CoreConstants } from '@core/constants';
|
||||||
import { FileEntry } from '@ionic-native/file';
|
|
||||||
import { makeSingleton } from '@singletons/core.singletons';
|
import { makeSingleton } from '@singletons/core.singletons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,23 +30,13 @@ import { makeSingleton } from '@singletons/core.singletons';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CoreFileHelperProvider {
|
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,
|
constructor(protected fileProvider: CoreFileProvider,
|
||||||
protected filepoolProvider: CoreFilepoolProvider,
|
protected filepoolProvider: CoreFilepoolProvider,
|
||||||
protected sitesProvider: CoreSitesProvider,
|
protected sitesProvider: CoreSitesProvider,
|
||||||
protected appProvider: CoreAppProvider,
|
protected appProvider: CoreAppProvider,
|
||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
protected utils: CoreUtilsProvider,
|
protected utils: CoreUtilsProvider,
|
||||||
protected wsProvider: CoreWSProvider,
|
protected wsProvider: CoreWSProvider) { }
|
||||||
protected configProvider: CoreConfigProvider,
|
|
||||||
protected domUtils: CoreDomUtilsProvider) {
|
|
||||||
|
|
||||||
this.initMaxChunkSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function to open a file, downloading it if needed.
|
* Convenience function to open a file, downloading it if needed.
|
||||||
|
@ -258,34 +245,6 @@ export class CoreFileHelperProvider {
|
||||||
return file.timemodified || 0;
|
return file.timemodified || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the max chunk size.
|
|
||||||
*
|
|
||||||
* @return Promise resolved when done.
|
|
||||||
*/
|
|
||||||
protected async initMaxChunkSize(): Promise<void> {
|
|
||||||
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.
|
* 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);
|
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<void> {
|
|
||||||
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<FileEntry> {
|
|
||||||
|
|
||||||
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<FileEntry> {
|
|
||||||
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) {}
|
export class CoreFileHelper extends makeSingleton(CoreFileHelperProvider) {}
|
||||||
|
|
|
@ -65,7 +65,7 @@ export class CoreFileProvider {
|
||||||
static SITESFOLDER = 'sites';
|
static SITESFOLDER = 'sites';
|
||||||
static TMPFOLDER = 'tmp';
|
static TMPFOLDER = 'tmp';
|
||||||
|
|
||||||
static CHUNK_SIZE = 10485760; // 10 MB.
|
static CHUNK_SIZE = 1048576; // 1 MB. Same chunk size as Ionic Native.
|
||||||
|
|
||||||
protected logger;
|
protected logger;
|
||||||
protected initialized = false;
|
protected initialized = false;
|
||||||
|
@ -639,6 +639,7 @@ export class CoreFileProvider {
|
||||||
/**
|
/**
|
||||||
* Write some file data into a filesystem file.
|
* Write some file data into a filesystem file.
|
||||||
* It's done in chunks to prevent crashing the app for big files.
|
* 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 file The data to write.
|
||||||
* @param path Path where to store the data.
|
* @param path Path where to store the data.
|
||||||
|
@ -646,19 +647,17 @@ export class CoreFileProvider {
|
||||||
* @param offset Offset where to start reading from.
|
* @param offset Offset where to start reading from.
|
||||||
* @param append Whether to append the data to the end of the file.
|
* @param append Whether to append the data to the end of the file.
|
||||||
* @return Promise resolved when done.
|
* @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,
|
async writeFileDataInFile(file: Blob, path: string, onProgress?: CoreFileProgressFunction, offset: number = 0,
|
||||||
append?: boolean): Promise<FileEntry> {
|
append?: boolean): Promise<FileEntry> {
|
||||||
|
|
||||||
offset = offset || 0;
|
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 {
|
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;
|
offset += CoreFileProvider.CHUNK_SIZE;
|
||||||
|
|
||||||
|
@ -676,32 +675,15 @@ export class CoreFileProvider {
|
||||||
// Read the next chunk.
|
// Read the next chunk.
|
||||||
return this.writeFileDataInFile(file, path, onProgress, offset, true);
|
return this.writeFileDataInFile(file, path, onProgress, offset, true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (readWholeFile || !error || error.name != 'NotReadableError') {
|
if (error && error.target && error.target.error) {
|
||||||
return Promise.reject(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.
|
throw error;
|
||||||
// Try to read the whole file at once.
|
|
||||||
return this.writeFileDataInFileChunk(file, path, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<FileEntry> {
|
|
||||||
// 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.
|
* Gets a file that might be outside the app's folder.
|
||||||
*
|
*
|
||||||
|
|
|
@ -901,7 +901,7 @@ export class CoreWSProvider {
|
||||||
|
|
||||||
return transfer.upload(filePath, uploadUrl, options, true).then((success) => {
|
return transfer.upload(filePath, uploadUrl, options, true).then((success) => {
|
||||||
const data = this.textUtils.parseJSON(success.response, null,
|
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) {
|
if (data === null) {
|
||||||
return Promise.reject(this.translate.instant('core.errorinvalidresponse'));
|
return Promise.reject(this.translate.instant('core.errorinvalidresponse'));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue