Skip to content

Commit 4da6a31

Browse files
committed
MOBILE-3401 file: Fix selecting files for offline with chooser
1 parent 68b9636 commit 4da6a31

File tree

3 files changed

+96
-77
lines changed

3 files changed

+96
-77
lines changed

src/core/fileuploader/providers/file-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class CoreFileUploaderFileHandler implements CoreFileUploaderHandler {
7474

7575
if (this.appProvider.isMobile()) {
7676
handler.action = (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise<any> => {
77-
return this.uploaderHelper.chooseAndUploadFile(maxSize, upload, mimetypes).then((result) => {
77+
return this.uploaderHelper.chooseAndUploadFile(maxSize, upload, allowOffline, mimetypes).then((result) => {
7878
return {
7979
treated: true,
8080
result: result

src/core/fileuploader/providers/helper.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ export class CoreFileUploaderHelperProvider {
6060
* @param maxSize Max size of the upload. -1 for no max size.
6161
* @param upload True if the file should be uploaded, false to return the picked file.
6262
* @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
63+
* @param allowOffline True to allow uploading in offline.
6364
* @return Promise resolved when done.
6465
*/
65-
async chooseAndUploadFile(maxSize: number, upload?: boolean, mimetypes?: string[]): Promise<any> {
66+
async chooseAndUploadFile(maxSize: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise<any> {
6667

6768
const result = await this.fileChooser.getFile(mimetypes ? mimetypes.join(',') : undefined);
6869

@@ -76,9 +77,28 @@ export class CoreFileUploaderHelperProvider {
7677
result.name = this.getChosenFileNameFromPath(result) || result.name;
7778
}
7879

79-
const options = this.fileUploaderProvider.getFileUploadOptions(result.uri, result.name, result.mediaType, true);
80+
// Verify that the mimetype is supported.
81+
const error = this.fileUploaderProvider.isInvalidMimetype(mimetypes, result.name, result.mediaType);
82+
83+
if (error) {
84+
return Promise.reject(error);
85+
}
86+
87+
if (upload) {
88+
const size = await this.fileProvider.getExternalFileSize(result.uri);
89+
90+
await this.confirmUploadFile(size, false, allowOffline);
91+
92+
const options = this.fileUploaderProvider.getFileUploadOptions(result.uri, result.name, result.mediaType, true);
8093

81-
return this.uploadFile(result.uri, maxSize, true, options);
94+
return this.uploadFile(result.uri, maxSize, true, options);
95+
} else {
96+
const entry = await this.fileProvider.getExternalFile(result.uri);
97+
98+
entry.name = result.name; // In Android sometimes the file is exported with a different name, use the original one.
99+
100+
return entry;
101+
}
82102
}
83103

84104
/**
@@ -664,15 +684,17 @@ export class CoreFileUploaderHelperProvider {
664684
* @param name Name to use when uploading the file. If not defined, use the file's name.
665685
* @return Promise resolved when done.
666686
*/
667-
uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise<any> {
687+
async uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise<any> {
668688
if (maxSize != -1 && file.size > maxSize) {
669689
return this.errorMaxBytes(maxSize, file.name);
670690
}
671691

672-
return this.confirmUploadFile(file.size, false, allowOffline).then(() => {
673-
// We have the data of the file to be uploaded, but not its URL (needed). Create a copy of the file to upload it.
674-
return this.copyAndUploadFile(file, upload, name);
675-
});
692+
if (upload) {
693+
await this.confirmUploadFile(file.size, false, allowOffline);
694+
}
695+
696+
// We have the data of the file to be uploaded, but not its URL (needed). Create a copy of the file to upload it.
697+
return this.copyAndUploadFile(file, upload, name);
676698
}
677699

678700
/**

src/providers/file.ts

Lines changed: 65 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,18 @@ export class CoreFileProvider {
696696
});
697697
}
698698

699+
/**
700+
* Calculate the size of a file.
701+
*
702+
* @param path Absolute path to the file.
703+
* @return Promise to be resolved when the size is calculated.
704+
*/
705+
async getExternalFileSize(path: string): Promise<number> {
706+
const fileEntry = await this.getExternalFile(path);
707+
708+
return this.getSize(fileEntry);
709+
}
710+
699711
/**
700712
* Removes a file that might be outside the app's folder.
701713
*
@@ -770,7 +782,7 @@ export class CoreFileProvider {
770782
* @return Promise resolved when the entry is moved.
771783
*/
772784
moveDir(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> {
773-
return this.moveFileOrDir(originalPath, newPath, true, destDirExists);
785+
return this.copyOrMoveFileOrDir(originalPath, newPath, true, false, destDirExists);
774786
}
775787

776788
/**
@@ -783,47 +795,7 @@ export class CoreFileProvider {
783795
* @return Promise resolved when the entry is moved.
784796
*/
785797
moveFile(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> {
786-
return this.moveFileOrDir(originalPath, newPath, false, destDirExists);
787-
}
788-
789-
/**
790-
* Move a file/dir.
791-
*
792-
* @param originalPath Path to the file/dir to move.
793-
* @param newPath New path of the file/dir.
794-
* @param isDir Whether it's a dir or a file.
795-
* @param destDirExists Set it to true if you know the directory where to put the file/dir exists. If false, the function will
796-
* try to create it (slower).
797-
* @return Promise resolved when the entry is moved.
798-
*/
799-
protected moveFileOrDir(originalPath: string, newPath: string, isDir?: boolean, destDirExists?: boolean): Promise<any> {
800-
const moveFn = isDir ? this.file.moveDir.bind(this.file) : this.file.moveFile.bind(this.file);
801-
802-
return this.init().then(() => {
803-
// Remove basePath if it's in the paths.
804-
originalPath = this.removeStartingSlash(originalPath.replace(this.basePath, ''));
805-
newPath = this.removeStartingSlash(newPath.replace(this.basePath, ''));
806-
807-
const newPathFileAndDir = this.getFileAndDirectoryFromPath(newPath);
808-
809-
if (newPathFileAndDir.directory && !destDirExists) {
810-
// Create the target directory if it doesn't exist.
811-
return this.createDir(newPathFileAndDir.directory);
812-
}
813-
}).then(() => {
814-
815-
return moveFn(this.basePath, originalPath, this.basePath, newPath).catch((error) => {
816-
// The move can fail if the path has encoded characters. Try again if that's the case.
817-
const decodedOriginal = decodeURI(originalPath),
818-
decodedNew = decodeURI(newPath);
819-
820-
if (decodedOriginal != originalPath || decodedNew != newPath) {
821-
return moveFn(this.basePath, decodedOriginal, this.basePath, decodedNew);
822-
} else {
823-
return Promise.reject(error);
824-
}
825-
});
826-
});
798+
return this.copyOrMoveFileOrDir(originalPath, newPath, false, false, destDirExists);
827799
}
828800

829801
/**
@@ -836,7 +808,7 @@ export class CoreFileProvider {
836808
* @return Promise resolved when the entry is copied.
837809
*/
838810
copyDir(from: string, to: string, destDirExists?: boolean): Promise<any> {
839-
return this.copyFileOrDir(from, to, true, destDirExists);
811+
return this.copyOrMoveFileOrDir(from, to, true, true, destDirExists);
840812
}
841813

842814
/**
@@ -849,46 +821,61 @@ export class CoreFileProvider {
849821
* @return Promise resolved when the entry is copied.
850822
*/
851823
copyFile(from: string, to: string, destDirExists?: boolean): Promise<any> {
852-
return this.copyFileOrDir(from, to, false, destDirExists);
824+
return this.copyOrMoveFileOrDir(from, to, false, true, destDirExists);
853825
}
854826

855827
/**
856-
* Copy a file or a directory.
828+
* Copy or move a file or a directory.
857829
*
858830
* @param from Path to the file/dir to move.
859831
* @param to New path of the file/dir.
860832
* @param isDir Whether it's a dir or a file.
833+
* @param copy Whether to copy. If false, it will move the file.
861834
* @param destDirExists Set it to true if you know the directory where to put the file/dir exists. If false, the function will
862835
* try to create it (slower).
863836
* @return Promise resolved when the entry is copied.
864837
*/
865-
protected copyFileOrDir(from: string, to: string, isDir?: boolean, destDirExists?: boolean): Promise<any> {
866-
const copyFn = isDir ? this.file.copyDir.bind(this.file) : this.file.copyFile.bind(this.file);
838+
protected async copyOrMoveFileOrDir(from: string, to: string, isDir?: boolean, copy?: boolean, destDirExists?: boolean)
839+
: Promise<Entry> {
867840

868-
return this.init().then(() => {
869-
// Paths cannot start with "/". Remove basePath if present.
870-
from = this.removeStartingSlash(from.replace(this.basePath, ''));
871-
to = this.removeStartingSlash(to.replace(this.basePath, ''));
841+
const fileIsInAppFolder = this.isPathInAppFolder(from);
872842

873-
const toFileAndDir = this.getFileAndDirectoryFromPath(to);
843+
if (!fileIsInAppFolder) {
844+
return this.copyOrMoveExternalFile(from, to, copy);
845+
}
874846

875-
if (toFileAndDir.directory && !destDirExists) {
876-
// Create the target directory if it doesn't exist.
877-
return this.createDir(toFileAndDir.directory);
878-
}
879-
}).then(() => {
880-
return copyFn(this.basePath, from, this.basePath, to).catch((error) => {
881-
// The copy can fail if the path has encoded characters. Try again if that's the case.
882-
const decodedFrom = decodeURI(from),
883-
decodedTo = decodeURI(to);
847+
const moveCopyFn = copy ?
848+
(isDir ? this.file.copyDir.bind(this.file) : this.file.copyFile.bind(this.file)) :
849+
(isDir ? this.file.moveDir.bind(this.file) : this.file.moveFile.bind(this.file));
884850

885-
if (from != decodedFrom || to != decodedTo) {
886-
return copyFn(this.basePath, decodedFrom, this.basePath, decodedTo);
887-
} else {
888-
return Promise.reject(error);
889-
}
890-
});
891-
});
851+
await this.init();
852+
853+
// Paths cannot start with "/". Remove basePath if present.
854+
from = this.removeStartingSlash(from.replace(this.basePath, ''));
855+
to = this.removeStartingSlash(to.replace(this.basePath, ''));
856+
857+
const toFileAndDir = this.getFileAndDirectoryFromPath(to);
858+
859+
if (toFileAndDir.directory && !destDirExists) {
860+
// Create the target directory if it doesn't exist.
861+
await this.createDir(toFileAndDir.directory);
862+
}
863+
864+
try {
865+
const entry = await moveCopyFn(this.basePath, from, this.basePath, to);
866+
867+
return entry;
868+
} catch (error) {
869+
// The copy can fail if the path has encoded characters. Try again if that's the case.
870+
const decodedFrom = decodeURI(from);
871+
const decodedTo = decodeURI(to);
872+
873+
if (from != decodedFrom || to != decodedTo) {
874+
return moveCopyFn(this.basePath, decodedFrom, this.basePath, decodedTo);
875+
} else {
876+
return Promise.reject(error);
877+
}
878+
}
892879
}
893880

894881
/**
@@ -1281,6 +1268,16 @@ export class CoreFileProvider {
12811268

12821269
return src.replace(CoreConfigConstants.ioswebviewscheme + '://localhost/_app_file_', 'file://');
12831270
}
1271+
1272+
/**
1273+
* Check if a certain path is in the app's folder (basePath).
1274+
*
1275+
* @param path Path to check.
1276+
* @return Whether it's in the app folder.
1277+
*/
1278+
protected isPathInAppFolder(path: string): boolean {
1279+
return !path || !path.match(/^[a-z0-9]+:\/\//i) || path.indexOf(this.basePath) != -1;
1280+
}
12841281
}
12851282

12861283
export class CoreFile extends makeSingleton(CoreFileProvider) {}

0 commit comments

Comments
 (0)