mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 18:12:09 +08:00
feat(file): copy sync and async support (#10273)
This commit is contained in:
@ -109,7 +109,8 @@ export function pickFile() {
|
||||
const file = args.intent.getData().toString();
|
||||
//const file = File.fromPath(args.intent.getData().toString());
|
||||
//console.log(file);
|
||||
readFile(file);
|
||||
//readFile(file);
|
||||
copyFile(file);
|
||||
}
|
||||
});
|
||||
const Intent = android.content.Intent;
|
||||
@ -172,6 +173,41 @@ function saveFile(selected, readSync) {
|
||||
console.log('==== SAVE END =========');
|
||||
}
|
||||
|
||||
function copyFile(file) {
|
||||
const picked = File.fromPath(file);
|
||||
const ext = picked.extension;
|
||||
const name = picked.name.replace(`.${ext}`, '');
|
||||
const tempCopy = File.fromPath(path.join(knownFolders.temp().path, `${name}-copy.${ext}`));
|
||||
|
||||
// const done = picked
|
||||
// .copySync(tempCopy.path);
|
||||
// console.log('done: ' + done + '\n' + 'Original path: ' + picked.path + '\n' + 'Copied to: ' + tempCopy.path + '\n' + 'Original size: ' + picked.size + '\n' + 'Copy size: ' + tempCopy.size + '\n');
|
||||
|
||||
picked
|
||||
.copy(tempCopy.path)
|
||||
.then((done) => {
|
||||
console.log('done: ' + done + '\n' + 'Original path: ' + picked.path + '\n' + 'Copied to: ' + tempCopy.path + '\n' + 'Original size: ' + picked.size + '\n' + 'Copy size: ' + tempCopy.size + '\n');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
export function copyTest() {
|
||||
const now = Date.now();
|
||||
const tempFile = File.fromPath(path.join(knownFolders.temp().path, `${now}.txt`));
|
||||
tempFile.writeTextSync('Hello World: ' + now);
|
||||
const tempCopy = File.fromPath(path.join(knownFolders.temp().path, `${now}-copy.txt`));
|
||||
tempFile
|
||||
.copy(tempCopy.path)
|
||||
.then((done) => {
|
||||
console.log('done: ' + done + '\n' + 'Original path: ' + tempFile.path + '\n' + 'Copied to: ' + tempCopy.path + '\n' + 'Original size: ' + tempFile.size + '\n' + 'Copy size: ' + tempCopy.size + '\n');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function getFileNameFromContent(content: string) {
|
||||
const file = getFileAccess().getFile(content);
|
||||
return decodeURIComponent(file.name).split('/').pop().toLowerCase();
|
||||
|
@ -4,5 +4,6 @@
|
||||
<Button text="Create Random" tap="createRandom" />
|
||||
<Button text="Pick File" tap="pickFile" />
|
||||
<Button text="Pick Multiple Files" tap="pickFiles" />
|
||||
<Button text="Test Copy" tap="copyTest" />
|
||||
</StackLayout>
|
||||
</Page>
|
||||
|
@ -254,6 +254,42 @@ export class FileSystemAccess implements IFileSystemAccess {
|
||||
return this.getLogicalRootPath() + '/app';
|
||||
}
|
||||
|
||||
public copy = this.copySync.bind(this);
|
||||
|
||||
public copySync(src: string, dest: string, onError?: (error: any) => any) {
|
||||
try {
|
||||
return org.nativescript.widgets.Async.File.copySync(src, dest, getApplicationContext());
|
||||
} catch (error) {
|
||||
if (onError) {
|
||||
onError(exception);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public copyAsync(src: string, dest: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
try {
|
||||
org.nativescript.widgets.Async.File.copy(
|
||||
src,
|
||||
dest,
|
||||
new org.nativescript.widgets.Async.CompleteCallback({
|
||||
onComplete: (result: boolean) => {
|
||||
resolve(result);
|
||||
},
|
||||
onError: (err) => {
|
||||
reject(new Error(err));
|
||||
},
|
||||
}),
|
||||
getApplicationContext()
|
||||
);
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public readBuffer = this.readBufferSync.bind(this);
|
||||
|
||||
public readBufferAsync(path: string): Promise<ArrayBuffer> {
|
||||
|
@ -2,6 +2,32 @@
|
||||
* An utility class used to provide methods to access and work with the file system.
|
||||
*/
|
||||
export interface IFileSystemAccess {
|
||||
/**
|
||||
* Copies a file to a given path.
|
||||
* @param src The path to the source file.
|
||||
* @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.
|
||||
*/
|
||||
copy(src: string, dest: string, onError?: (error: any) => any): any;
|
||||
|
||||
/**
|
||||
* Copies a file to a given path.
|
||||
* @param src The path to the source file.
|
||||
* @param dest The path to the destination file.
|
||||
* Returns a Promise with a boolean.
|
||||
*/
|
||||
copyAsync(src: string, dest: string): Promise<any>;
|
||||
|
||||
/**
|
||||
* Copies a file to a given path.
|
||||
* @param src The path to the source file.
|
||||
* @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(src: string, dest: string, onError?: (error: any) => any): any;
|
||||
|
||||
/**
|
||||
* Gets the last modified date of a file with a given path.
|
||||
* @param path Path to the file.
|
||||
@ -256,6 +282,12 @@ export interface IFileSystemAccess {
|
||||
}
|
||||
|
||||
export class FileSystemAccess implements IFileSystemAccess {
|
||||
copy(src: string, dest: string, onError?: (error: any) => any): boolean;
|
||||
|
||||
copySync(src: string, dest: string, onError?: (error: any) => any): boolean;
|
||||
|
||||
copyAsync(src: string, dest: string): Promise<boolean>;
|
||||
|
||||
getLastModified(path: string): Date;
|
||||
|
||||
getFileSize(path: string): number;
|
||||
|
@ -260,6 +260,65 @@ export class FileSystemAccess {
|
||||
return iOSNativeHelper.getCurrentAppPath();
|
||||
}
|
||||
|
||||
public copy = this.copySync.bind(this);
|
||||
|
||||
public copySync(src: string, dest: string, onError?: (error: any) => any) {
|
||||
const fileManager = NSFileManager.defaultManager;
|
||||
try {
|
||||
return fileManager.copyItemAtPathToPathError(src, dest);
|
||||
} catch (error) {
|
||||
if (error.message.indexOf('exists') > -1) {
|
||||
// check the size of file if empty remove then try copying again
|
||||
// this could be zero due to using File.fromPath passing in a new file
|
||||
let didRemove = false;
|
||||
try {
|
||||
didRemove = fileManager.removeItemAtPathError(dest);
|
||||
return fileManager.copyItemAtPathToPathError(src, dest);
|
||||
} catch (error) {
|
||||
if (onError) {
|
||||
if (didRemove) {
|
||||
onError(error);
|
||||
} else {
|
||||
onError(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (onError) {
|
||||
onError(exception);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public copyAsync(src: string, dest: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
try {
|
||||
NSData.dataWithContentsOfFileCompletion(src, (data) => {
|
||||
if (!data) {
|
||||
reject(new Error("Failed to read file at path '" + src));
|
||||
} else {
|
||||
data.writeToFileAtomicallyCompletion(dest, true, () => {
|
||||
if (this.fileExists(dest)) {
|
||||
const size = this.getFileSize(dest);
|
||||
if (size === data.length) {
|
||||
resolve(true);
|
||||
} else {
|
||||
reject(new Error("Failed to write file at path '" + dest));
|
||||
}
|
||||
} else {
|
||||
reject(new Error("Failed to write file at path '" + dest));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public readText = this.readTextSync.bind(this);
|
||||
|
||||
public readTextAsync(path: string, encoding?: any) {
|
||||
|
@ -211,6 +211,53 @@ export class File extends FileSystemEntity {
|
||||
return getFileAccess().getFileSize(this.path);
|
||||
}
|
||||
|
||||
public copy(dest: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
try {
|
||||
this._checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._locked = true;
|
||||
|
||||
getFileAccess()
|
||||
.copyAsync(this.path, dest)
|
||||
.then(
|
||||
(result) => {
|
||||
resolve(result);
|
||||
this._locked = false;
|
||||
},
|
||||
(error) => {
|
||||
reject(error);
|
||||
this._locked = false;
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public copySync(dest: string, onError?: (error: any) => any): any {
|
||||
this._checkAccess();
|
||||
|
||||
this._locked = true;
|
||||
|
||||
const that = this;
|
||||
const localError = (error) => {
|
||||
that._locked = false;
|
||||
if (onError) {
|
||||
onError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const content = getFileAccess().copySync(this.path, dest, localError);
|
||||
|
||||
this._locked = false;
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public read(): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
try {
|
||||
|
Binary file not shown.
@ -29,6 +29,10 @@
|
||||
}
|
||||
|
||||
export module File {
|
||||
export function copySync(src: string, dest: string, context: android.content.Context): boolean;
|
||||
export function copy(src: java.io.InputStream, dest: java.io.OutputStream, callback: org.nativescript.widgets.Async.CompleteCallback, context: any): void;
|
||||
export function copySync(src: java.io.InputStream, dest: java.io.OutputStream, context: any): boolean;
|
||||
export function copy(src: string, dest: string, callback: org.nativescript.widgets.Async.CompleteCallback, context: android.content.Context): void;
|
||||
export function readText(path: string, encoding: string, callback: CompleteCallback, context: any);
|
||||
export function read(path: string, callback: CompleteCallback, context: any);
|
||||
export function readBuffer(param0: string, param1: org.nativescript.widgets.Async.CompleteCallback, param2: any): void;
|
||||
|
@ -5,6 +5,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Looper;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
@ -13,6 +14,7 @@ import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
@ -29,6 +31,8 @@ import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -595,6 +599,94 @@ public class Async {
|
||||
|
||||
public static class File {
|
||||
|
||||
public static boolean copySync(final String src, final String dest, final Context context) throws Exception {
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
|
||||
if(src.startsWith("content://")){
|
||||
is = context.getContentResolver().openInputStream(Uri.parse(src));
|
||||
}else is = new FileInputStream(new java.io.File(src));
|
||||
|
||||
if(dest.startsWith("content://")){
|
||||
os = context.getContentResolver().openOutputStream(Uri.parse(dest));
|
||||
}else os = new FileOutputStream(new java.io.File(dest));
|
||||
|
||||
return copySync(is, os, context);
|
||||
}
|
||||
|
||||
public static boolean copySync(final InputStream src, final OutputStream dest, final Object context) throws Exception {
|
||||
ReadableByteChannel isc = java.nio.channels.Channels.newChannel(src);
|
||||
WritableByteChannel osc = java.nio.channels.Channels.newChannel(dest);
|
||||
|
||||
int size = src.available();
|
||||
|
||||
int written = fastChannelCopy(isc, osc);
|
||||
|
||||
return size == written;
|
||||
}
|
||||
|
||||
public static void copy(final String src, final String dest, final CompleteCallback callback, final Context context) {
|
||||
try {
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
|
||||
if(src.startsWith("content://")){
|
||||
is = context.getContentResolver().openInputStream(Uri.parse(src));
|
||||
}else is = new FileInputStream(new java.io.File(src));
|
||||
|
||||
if(dest.startsWith("content://")){
|
||||
os = context.getContentResolver().openOutputStream(Uri.parse(dest));
|
||||
}else os = new FileOutputStream(new java.io.File(dest));
|
||||
|
||||
copy(is, os, callback, context);
|
||||
}catch (Exception exception){
|
||||
callback.onError(exception.getMessage(), context);
|
||||
}
|
||||
}
|
||||
|
||||
private static int fastChannelCopy(final ReadableByteChannel src,
|
||||
final WritableByteChannel dest) throws IOException {
|
||||
int written = 0;
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
|
||||
while (src.read(buffer) != -1) {
|
||||
// prepare the buffer to be drained
|
||||
buffer.flip();
|
||||
// write to the channel, may block
|
||||
written += dest.write(buffer);
|
||||
// If partial transfer, shift remainder down
|
||||
// If buffer is empty, same as doing clear()
|
||||
buffer.compact();
|
||||
}
|
||||
// EOF will leave buffer in fill state
|
||||
buffer.flip();
|
||||
// make sure the buffer is fully drained.
|
||||
while (buffer.hasRemaining()) {
|
||||
written += dest.write(buffer);
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
public static void copy(final InputStream src, final OutputStream dest, final CompleteCallback callback, final Object context) {
|
||||
final android.os.Handler mHandler = new android.os.Handler(Looper.myLooper());
|
||||
threadPoolExecutor().execute((Runnable) () -> {
|
||||
|
||||
try (InputStream is = src; OutputStream os = dest){
|
||||
ReadableByteChannel isc = java.nio.channels.Channels.newChannel(is);
|
||||
WritableByteChannel osc = java.nio.channels.Channels.newChannel(os);
|
||||
|
||||
int size = src.available();
|
||||
|
||||
int written = fastChannelCopy(isc, osc);
|
||||
|
||||
mHandler.post(() -> callback.onComplete(size == written, context));
|
||||
|
||||
} catch (Exception e) {
|
||||
mHandler.post(() -> callback.onError(e.getMessage(), context));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void readText(final String path, final String encoding, final CompleteCallback callback, final Object context) {
|
||||
final android.os.Handler mHandler = new android.os.Handler(Looper.myLooper());
|
||||
threadPoolExecutor().execute(new Runnable() {
|
||||
|
Reference in New Issue
Block a user