mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 18:12:09 +08:00
feat(file-system): allow copy when opening a File (#10274)
* feat: add copy file to file-system * feat(file-system): allow temp copy of files opened with File.fromPath * chore: remove log * chore: remove log * fix: only copy if true --------- Co-authored-by: Nathan Walker <walkerrunpdx@gmail.com>
This commit is contained in:
@ -698,3 +698,24 @@ export function test_FolderClear_RemovesEmptySubfolders(done) {
|
|||||||
})
|
})
|
||||||
.catch(done);
|
.catch(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function test_FileCopy(done) {
|
||||||
|
const now = Date.now();
|
||||||
|
const tempFile = fs.File.fromPath(fs.path.join(fs.knownFolders.temp().path, `${now}.txt`));
|
||||||
|
const content = 'Hello World: ' + now;
|
||||||
|
tempFile.writeTextSync(content);
|
||||||
|
const tempCopy = fs.File.fromPath(fs.path.join(fs.knownFolders.temp().path, `${now}-copy.txt`));
|
||||||
|
tempFile
|
||||||
|
.copy(tempCopy.path)
|
||||||
|
.then(() => {
|
||||||
|
TKUnit.assert(tempCopy.size === tempFile.size);
|
||||||
|
return tempCopy.readText();
|
||||||
|
})
|
||||||
|
.then((value) => {
|
||||||
|
TKUnit.assert(value === content);
|
||||||
|
|
||||||
|
return Promise.allSettled([tempFile.remove(), tempCopy.remove()]);
|
||||||
|
})
|
||||||
|
.then(() => done())
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
@ -110,7 +110,12 @@ export function pickFile() {
|
|||||||
//const file = File.fromPath(args.intent.getData().toString());
|
//const file = File.fromPath(args.intent.getData().toString());
|
||||||
//console.log(file);
|
//console.log(file);
|
||||||
//readFile(file);
|
//readFile(file);
|
||||||
copyFile(file);
|
//copyFile(file);
|
||||||
|
console.time('fromPath: copy');
|
||||||
|
const f = File.fromPath(file, true);
|
||||||
|
console.timeEnd('fromPath: copy');
|
||||||
|
console.log('old path: ', file);
|
||||||
|
console.log('new path: ', f.path, f.extension, f.size);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const Intent = android.content.Intent;
|
const Intent = android.content.Intent;
|
||||||
|
@ -15,7 +15,7 @@ function getApplicationContext() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getOrSetHelper(path: string): org.nativescript.widgets.FileHelper {
|
function getOrSetHelper(path: string): org.nativescript.widgets.FileHelper {
|
||||||
return org.nativescript.widgets.FileHelper.fromString(applicationContext, path);
|
return org.nativescript.widgets.FileHelper.fromString(getApplicationContext(), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isContentUri(path: string): boolean {
|
function isContentUri(path: string): boolean {
|
||||||
@ -766,6 +766,7 @@ export class FileSystemAccess29 extends FileSystemAccess {
|
|||||||
if (isContentUri(path)) {
|
if (isContentUri(path)) {
|
||||||
try {
|
try {
|
||||||
const file = getOrSetHelper(path);
|
const file = getOrSetHelper(path);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path,
|
path,
|
||||||
name: file.getName(),
|
name: file.getName(),
|
||||||
|
18
packages/core/file-system/index.d.ts
vendored
18
packages/core/file-system/index.d.ts
vendored
@ -80,11 +80,27 @@ export class File extends FileSystemEntity {
|
|||||||
*/
|
*/
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies a file to a given path.
|
||||||
|
* @param dest The path to the destination file.
|
||||||
|
* Returns a Promise with a boolean.
|
||||||
|
*/
|
||||||
|
copy(dest: string): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies a file to a given path.
|
||||||
|
* @param dest The path to the destination file.
|
||||||
|
* @param onError (optional) A callback function to use if any error occurs.
|
||||||
|
* Returns a Promise with a boolean.
|
||||||
|
*/
|
||||||
|
copySync(dest: string, onError?: (error: any) => any): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or creates a File entity at the specified path.
|
* Gets or creates a File entity at the specified path.
|
||||||
* @param path The path to get/create the file at.
|
* @param path The path to get/create the file at.
|
||||||
|
* @param copy An optional value when set, copies the content-uri to a temp file enabling the legacy behaviour
|
||||||
*/
|
*/
|
||||||
static fromPath(path: string): File;
|
static fromPath(path: string, copy?: boolean): File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the content of the file as a string using the specified encoding (defaults to UTF-8).
|
* Reads the content of the file as a string using the specified encoding (defaults to UTF-8).
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { IFileSystemAccess, FileSystemAccess, FileSystemAccess29 } from './file-system-access';
|
import { IFileSystemAccess, FileSystemAccess, FileSystemAccess29 } from './file-system-access';
|
||||||
import { SDK_VERSION } from '../utils/constants';
|
import { SDK_VERSION } from '../utils/constants';
|
||||||
|
import { getNativeApplication } from '../application';
|
||||||
// The FileSystemAccess implementation, used through all the APIs.
|
// The FileSystemAccess implementation, used through all the APIs.
|
||||||
let fileAccess: IFileSystemAccess;
|
let fileAccess: IFileSystemAccess;
|
||||||
|
|
||||||
@ -180,12 +181,39 @@ export class FileSystemEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let applicationContext;
|
||||||
|
function getApplicationContext() {
|
||||||
|
if (!applicationContext) {
|
||||||
|
applicationContext = (<android.app.Application>getNativeApplication()).getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
export class File extends FileSystemEntity {
|
export class File extends FileSystemEntity {
|
||||||
public static fromPath(path: string) {
|
public static fromPath(path: string, copy: boolean = false) {
|
||||||
const onError = function (error) {
|
const onError = function (error) {
|
||||||
throw error;
|
throw error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (global.isAndroid && copy) {
|
||||||
|
if (path.startsWith('content:')) {
|
||||||
|
const fileInfo = getFileAccess().getFile(path, onError);
|
||||||
|
// falls back to creating a temp file without a known extension.
|
||||||
|
if (!fileInfo) {
|
||||||
|
const tempFile = `${knownFolders.temp().path}/${java.util.UUID.randomUUID().toString()}`;
|
||||||
|
org.nativescript.widgets.Async.File.copySync(path, tempFile, getApplicationContext());
|
||||||
|
path = tempFile;
|
||||||
|
} else {
|
||||||
|
const ext = fileInfo.extension;
|
||||||
|
const name = `${fileInfo.name.replace(`.${ext}`, '')}.${ext}`;
|
||||||
|
const tempFile = `${knownFolders.temp().path}/${name}`;
|
||||||
|
getFileAccess().copySync(path, tempFile);
|
||||||
|
path = tempFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const fileInfo = getFileAccess().getFile(path, onError);
|
const fileInfo = getFileAccess().getFile(path, onError);
|
||||||
if (!fileInfo) {
|
if (!fileInfo) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
Binary file not shown.
@ -53,9 +53,8 @@ public class FileHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isExternalStorageDocument(Uri uri) {
|
private static boolean isExternalStorageDocument(Uri uri) {
|
||||||
return false;
|
return "com.android.externalstorage.documents".equals(uri
|
||||||
// return "com.android.externalstorage.documents".equals(uri
|
.getAuthority());
|
||||||
// .getAuthority());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable
|
private static @Nullable
|
||||||
@ -132,7 +131,9 @@ public class FileHelper {
|
|||||||
String path = split[1];
|
String path = split[1];
|
||||||
|
|
||||||
if ("primary".equals(type)) {
|
if ("primary".equals(type)) {
|
||||||
String[] parts = Uri.decode(uri.toString()).split(":" + path + "/");
|
int nameIndex = path.lastIndexOf("/");
|
||||||
|
String seg = path.substring(0, nameIndex);
|
||||||
|
String[] parts = Uri.decode(uri.toString()).split(":" + seg);
|
||||||
String file = Environment.getExternalStorageDirectory() + "/" + path + "/" + parts[1];
|
String file = Environment.getExternalStorageDirectory() + "/" + path + "/" + parts[1];
|
||||||
return new File(file);
|
return new File(file);
|
||||||
} else {
|
} else {
|
||||||
@ -178,6 +179,9 @@ public class FileHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean moved = cursor.moveToFirst();
|
||||||
|
FileHelper helper = null;
|
||||||
|
if (moved) {
|
||||||
int sizeIndex = cursor.getColumnIndex(
|
int sizeIndex = cursor.getColumnIndex(
|
||||||
MediaStore.MediaColumns.SIZE
|
MediaStore.MediaColumns.SIZE
|
||||||
);
|
);
|
||||||
@ -190,10 +194,6 @@ public class FileHelper {
|
|||||||
MediaStore.MediaColumns.DATE_MODIFIED
|
MediaStore.MediaColumns.DATE_MODIFIED
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
boolean moved = cursor.moveToFirst();
|
|
||||||
FileHelper helper = null;
|
|
||||||
if (moved) {
|
|
||||||
helper = new FileHelper(uri);
|
helper = new FileHelper(uri);
|
||||||
helper.size = cursor.getLong(sizeIndex);
|
helper.size = cursor.getLong(sizeIndex);
|
||||||
helper.name = cursor.getString(nameIndex);
|
helper.name = cursor.getString(nameIndex);
|
||||||
@ -244,6 +244,9 @@ public class FileHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean moved = cursor.moveToFirst();
|
||||||
|
|
||||||
|
if (moved) {
|
||||||
int sizeIndex = cursor.getColumnIndex(
|
int sizeIndex = cursor.getColumnIndex(
|
||||||
MediaStore.MediaColumns.SIZE
|
MediaStore.MediaColumns.SIZE
|
||||||
);
|
);
|
||||||
@ -256,10 +259,6 @@ public class FileHelper {
|
|||||||
MediaStore.MediaColumns.DATE_MODIFIED
|
MediaStore.MediaColumns.DATE_MODIFIED
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
boolean moved = cursor.moveToFirst();
|
|
||||||
|
|
||||||
if (moved) {
|
|
||||||
size = cursor.getLong(sizeIndex);
|
size = cursor.getLong(sizeIndex);
|
||||||
name = cursor.getString(nameIndex);
|
name = cursor.getString(nameIndex);
|
||||||
mime = context.getContentResolver().getType(uri);
|
mime = context.getContentResolver().getType(uri);
|
||||||
|
Reference in New Issue
Block a user