MOBILE-3401 file: Fix selecting files for offline with chooser
parent
68b963638a
commit
4da6a31168
|
@ -74,7 +74,7 @@ export class CoreFileUploaderFileHandler implements CoreFileUploaderHandler {
|
|||
|
||||
if (this.appProvider.isMobile()) {
|
||||
handler.action = (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise<any> => {
|
||||
return this.uploaderHelper.chooseAndUploadFile(maxSize, upload, mimetypes).then((result) => {
|
||||
return this.uploaderHelper.chooseAndUploadFile(maxSize, upload, allowOffline, mimetypes).then((result) => {
|
||||
return {
|
||||
treated: true,
|
||||
result: result
|
||||
|
|
|
@ -60,9 +60,10 @@ export class CoreFileUploaderHelperProvider {
|
|||
* @param maxSize Max size of the upload. -1 for no max size.
|
||||
* @param upload True if the file should be uploaded, false to return the picked file.
|
||||
* @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
|
||||
* @param allowOffline True to allow uploading in offline.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async chooseAndUploadFile(maxSize: number, upload?: boolean, mimetypes?: string[]): Promise<any> {
|
||||
async chooseAndUploadFile(maxSize: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise<any> {
|
||||
|
||||
const result = await this.fileChooser.getFile(mimetypes ? mimetypes.join(',') : undefined);
|
||||
|
||||
|
@ -76,9 +77,28 @@ export class CoreFileUploaderHelperProvider {
|
|||
result.name = this.getChosenFileNameFromPath(result) || result.name;
|
||||
}
|
||||
|
||||
const options = this.fileUploaderProvider.getFileUploadOptions(result.uri, result.name, result.mediaType, true);
|
||||
// Verify that the mimetype is supported.
|
||||
const error = this.fileUploaderProvider.isInvalidMimetype(mimetypes, result.name, result.mediaType);
|
||||
|
||||
return this.uploadFile(result.uri, maxSize, true, options);
|
||||
if (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (upload) {
|
||||
const size = await this.fileProvider.getExternalFileSize(result.uri);
|
||||
|
||||
await this.confirmUploadFile(size, false, allowOffline);
|
||||
|
||||
const options = this.fileUploaderProvider.getFileUploadOptions(result.uri, result.name, result.mediaType, true);
|
||||
|
||||
return this.uploadFile(result.uri, maxSize, true, options);
|
||||
} else {
|
||||
const entry = await this.fileProvider.getExternalFile(result.uri);
|
||||
|
||||
entry.name = result.name; // In Android sometimes the file is exported with a different name, use the original one.
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -664,15 +684,17 @@ export class CoreFileUploaderHelperProvider {
|
|||
* @param name Name to use when uploading the file. If not defined, use the file's name.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise<any> {
|
||||
async uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise<any> {
|
||||
if (maxSize != -1 && file.size > maxSize) {
|
||||
return this.errorMaxBytes(maxSize, file.name);
|
||||
}
|
||||
|
||||
return this.confirmUploadFile(file.size, false, allowOffline).then(() => {
|
||||
// We have the data of the file to be uploaded, but not its URL (needed). Create a copy of the file to upload it.
|
||||
return this.copyAndUploadFile(file, upload, name);
|
||||
});
|
||||
if (upload) {
|
||||
await this.confirmUploadFile(file.size, false, allowOffline);
|
||||
}
|
||||
|
||||
// We have the data of the file to be uploaded, but not its URL (needed). Create a copy of the file to upload it.
|
||||
return this.copyAndUploadFile(file, upload, name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -696,6 +696,18 @@ export class CoreFileProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the size of a file.
|
||||
*
|
||||
* @param path Absolute path to the file.
|
||||
* @return Promise to be resolved when the size is calculated.
|
||||
*/
|
||||
async getExternalFileSize(path: string): Promise<number> {
|
||||
const fileEntry = await this.getExternalFile(path);
|
||||
|
||||
return this.getSize(fileEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file that might be outside the app's folder.
|
||||
*
|
||||
|
@ -770,7 +782,7 @@ export class CoreFileProvider {
|
|||
* @return Promise resolved when the entry is moved.
|
||||
*/
|
||||
moveDir(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> {
|
||||
return this.moveFileOrDir(originalPath, newPath, true, destDirExists);
|
||||
return this.copyOrMoveFileOrDir(originalPath, newPath, true, false, destDirExists);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -783,47 +795,7 @@ export class CoreFileProvider {
|
|||
* @return Promise resolved when the entry is moved.
|
||||
*/
|
||||
moveFile(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> {
|
||||
return this.moveFileOrDir(originalPath, newPath, false, destDirExists);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a file/dir.
|
||||
*
|
||||
* @param originalPath Path to the file/dir to move.
|
||||
* @param newPath New path of the file/dir.
|
||||
* @param isDir Whether it's a dir or a file.
|
||||
* @param destDirExists Set it to true if you know the directory where to put the file/dir exists. If false, the function will
|
||||
* try to create it (slower).
|
||||
* @return Promise resolved when the entry is moved.
|
||||
*/
|
||||
protected moveFileOrDir(originalPath: string, newPath: string, isDir?: boolean, destDirExists?: boolean): Promise<any> {
|
||||
const moveFn = isDir ? this.file.moveDir.bind(this.file) : this.file.moveFile.bind(this.file);
|
||||
|
||||
return this.init().then(() => {
|
||||
// Remove basePath if it's in the paths.
|
||||
originalPath = this.removeStartingSlash(originalPath.replace(this.basePath, ''));
|
||||
newPath = this.removeStartingSlash(newPath.replace(this.basePath, ''));
|
||||
|
||||
const newPathFileAndDir = this.getFileAndDirectoryFromPath(newPath);
|
||||
|
||||
if (newPathFileAndDir.directory && !destDirExists) {
|
||||
// Create the target directory if it doesn't exist.
|
||||
return this.createDir(newPathFileAndDir.directory);
|
||||
}
|
||||
}).then(() => {
|
||||
|
||||
return moveFn(this.basePath, originalPath, this.basePath, newPath).catch((error) => {
|
||||
// The move can fail if the path has encoded characters. Try again if that's the case.
|
||||
const decodedOriginal = decodeURI(originalPath),
|
||||
decodedNew = decodeURI(newPath);
|
||||
|
||||
if (decodedOriginal != originalPath || decodedNew != newPath) {
|
||||
return moveFn(this.basePath, decodedOriginal, this.basePath, decodedNew);
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
return this.copyOrMoveFileOrDir(originalPath, newPath, false, false, destDirExists);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -836,7 +808,7 @@ export class CoreFileProvider {
|
|||
* @return Promise resolved when the entry is copied.
|
||||
*/
|
||||
copyDir(from: string, to: string, destDirExists?: boolean): Promise<any> {
|
||||
return this.copyFileOrDir(from, to, true, destDirExists);
|
||||
return this.copyOrMoveFileOrDir(from, to, true, true, destDirExists);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -849,46 +821,61 @@ export class CoreFileProvider {
|
|||
* @return Promise resolved when the entry is copied.
|
||||
*/
|
||||
copyFile(from: string, to: string, destDirExists?: boolean): Promise<any> {
|
||||
return this.copyFileOrDir(from, to, false, destDirExists);
|
||||
return this.copyOrMoveFileOrDir(from, to, false, true, destDirExists);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file or a directory.
|
||||
* Copy or move a file or a directory.
|
||||
*
|
||||
* @param from Path to the file/dir to move.
|
||||
* @param to New path of the file/dir.
|
||||
* @param isDir Whether it's a dir or a file.
|
||||
* @param copy Whether to copy. If false, it will move the file.
|
||||
* @param destDirExists Set it to true if you know the directory where to put the file/dir exists. If false, the function will
|
||||
* try to create it (slower).
|
||||
* @return Promise resolved when the entry is copied.
|
||||
*/
|
||||
protected copyFileOrDir(from: string, to: string, isDir?: boolean, destDirExists?: boolean): Promise<any> {
|
||||
const copyFn = isDir ? this.file.copyDir.bind(this.file) : this.file.copyFile.bind(this.file);
|
||||
protected async copyOrMoveFileOrDir(from: string, to: string, isDir?: boolean, copy?: boolean, destDirExists?: boolean)
|
||||
: Promise<Entry> {
|
||||
|
||||
return this.init().then(() => {
|
||||
// Paths cannot start with "/". Remove basePath if present.
|
||||
from = this.removeStartingSlash(from.replace(this.basePath, ''));
|
||||
to = this.removeStartingSlash(to.replace(this.basePath, ''));
|
||||
const fileIsInAppFolder = this.isPathInAppFolder(from);
|
||||
|
||||
const toFileAndDir = this.getFileAndDirectoryFromPath(to);
|
||||
if (!fileIsInAppFolder) {
|
||||
return this.copyOrMoveExternalFile(from, to, copy);
|
||||
}
|
||||
|
||||
if (toFileAndDir.directory && !destDirExists) {
|
||||
// Create the target directory if it doesn't exist.
|
||||
return this.createDir(toFileAndDir.directory);
|
||||
const moveCopyFn = copy ?
|
||||
(isDir ? this.file.copyDir.bind(this.file) : this.file.copyFile.bind(this.file)) :
|
||||
(isDir ? this.file.moveDir.bind(this.file) : this.file.moveFile.bind(this.file));
|
||||
|
||||
await this.init();
|
||||
|
||||
// Paths cannot start with "/". Remove basePath if present.
|
||||
from = this.removeStartingSlash(from.replace(this.basePath, ''));
|
||||
to = this.removeStartingSlash(to.replace(this.basePath, ''));
|
||||
|
||||
const toFileAndDir = this.getFileAndDirectoryFromPath(to);
|
||||
|
||||
if (toFileAndDir.directory && !destDirExists) {
|
||||
// Create the target directory if it doesn't exist.
|
||||
await this.createDir(toFileAndDir.directory);
|
||||
}
|
||||
|
||||
try {
|
||||
const entry = await moveCopyFn(this.basePath, from, this.basePath, to);
|
||||
|
||||
return entry;
|
||||
} catch (error) {
|
||||
// The copy can fail if the path has encoded characters. Try again if that's the case.
|
||||
const decodedFrom = decodeURI(from);
|
||||
const decodedTo = decodeURI(to);
|
||||
|
||||
if (from != decodedFrom || to != decodedTo) {
|
||||
return moveCopyFn(this.basePath, decodedFrom, this.basePath, decodedTo);
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}).then(() => {
|
||||
return copyFn(this.basePath, from, this.basePath, to).catch((error) => {
|
||||
// The copy can fail if the path has encoded characters. Try again if that's the case.
|
||||
const decodedFrom = decodeURI(from),
|
||||
decodedTo = decodeURI(to);
|
||||
|
||||
if (from != decodedFrom || to != decodedTo) {
|
||||
return copyFn(this.basePath, decodedFrom, this.basePath, decodedTo);
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1281,6 +1268,16 @@ export class CoreFileProvider {
|
|||
|
||||
return src.replace(CoreConfigConstants.ioswebviewscheme + '://localhost/_app_file_', 'file://');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a certain path is in the app's folder (basePath).
|
||||
*
|
||||
* @param path Path to check.
|
||||
* @return Whether it's in the app folder.
|
||||
*/
|
||||
protected isPathInAppFolder(path: string): boolean {
|
||||
return !path || !path.match(/^[a-z0-9]+:\/\//i) || path.indexOf(this.basePath) != -1;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreFile extends makeSingleton(CoreFileProvider) {}
|
||||
|
|
Loading…
Reference in New Issue