mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 19:26:42 +08:00
feat(file-system): async read/write (#7671)
This commit is contained in:
@ -201,6 +201,46 @@ export var testFileReadWriteBinary = function () {
|
||||
// << file-system-read-binary
|
||||
};
|
||||
|
||||
export var testFileReadWriteBinaryAsync = function () {
|
||||
// >> file-system-read-binary-async
|
||||
var fileName = "logo.png";
|
||||
|
||||
var sourceFile = fs.File.fromPath(__dirname + "/assets/" + fileName);
|
||||
var destinationFile = fs.knownFolders.documents().getFile(fileName);
|
||||
|
||||
// Read the file
|
||||
sourceFile.read()
|
||||
.then(function (source) {
|
||||
// Succeeded in reading the file
|
||||
// >> (hide)
|
||||
destinationFile.write(source).then(function () {
|
||||
// Succeded in writing the file
|
||||
destinationFile.read()
|
||||
.then(function (destination) {
|
||||
if (platform.device.os === platform.platformNames.ios) {
|
||||
TKUnit.assertTrue(source.isEqualToData(destination));
|
||||
} else {
|
||||
TKUnit.assertEqual(new java.io.File(sourceFile.path).length(), new java.io.File(destinationFile.path).length());
|
||||
}
|
||||
|
||||
destinationFile.removeSync();
|
||||
}, function (error) {
|
||||
TKUnit.assert(false, "Failed to read destination binary async");
|
||||
});
|
||||
}, function (error) {
|
||||
// Failed to write the file.
|
||||
TKUnit.assert(false, "Failed to write binary async");
|
||||
});
|
||||
// << (hide)
|
||||
}, function (error) {
|
||||
// Failed to read the file.
|
||||
// >> (hide)
|
||||
TKUnit.assert(false, "Failed to read binary async");
|
||||
// << (hide)
|
||||
});
|
||||
// << file-system-read-binary-async
|
||||
};
|
||||
|
||||
export var testGetKnownFolders = function () {
|
||||
// >> file-system-known-folders
|
||||
// Getting the application's 'documents' folder.
|
||||
|
@ -11,15 +11,22 @@ import android.util.Log;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.CookieHandler;
|
||||
import java.net.CookieManager;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -585,4 +592,278 @@ public class Async {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class File {
|
||||
|
||||
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();
|
||||
threadPoolExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final ReadTextTask task = new ReadTextTask(callback, context);
|
||||
final String result = task.doInBackground(path, encoding);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
task.onPostExecute(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void read(final String path, final CompleteCallback callback, final Object context) {
|
||||
final android.os.Handler mHandler = new android.os.Handler();
|
||||
threadPoolExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final ReadTask task = new ReadTask(callback, context);
|
||||
final byte[] result = task.doInBackground(path);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
task.onPostExecute(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void writeText(final String path, final String content, final String encoding, final CompleteCallback callback, final Object context) {
|
||||
final android.os.Handler mHandler = new android.os.Handler();
|
||||
threadPoolExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final WriteTextTask task = new WriteTextTask(callback, context);
|
||||
final boolean result = task.doInBackground(path, content, encoding);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
task.onPostExecute(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void write(final String path, final byte[] content, final CompleteCallback callback, final Object context) {
|
||||
final android.os.Handler mHandler = new android.os.Handler();
|
||||
threadPoolExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final WriteTask task = new WriteTask(callback, context);
|
||||
final boolean result = task.doInBackground(path, content);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
task.onPostExecute(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class ReadTextTask {
|
||||
private CompleteCallback callback;
|
||||
private Object context;
|
||||
|
||||
public ReadTextTask(CompleteCallback callback, Object context) {
|
||||
this.callback = callback;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected String doInBackground(String... params) {
|
||||
java.io.File javaFile = new java.io.File(params[0]);
|
||||
FileInputStream stream = null;
|
||||
|
||||
try {
|
||||
stream = new FileInputStream(javaFile);
|
||||
|
||||
InputStreamReader reader = new InputStreamReader(stream, params[1]);
|
||||
|
||||
CharBuffer buffer = CharBuffer.allocate(81920);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
while (reader.read(buffer) != -1) {
|
||||
buffer.flip();
|
||||
sb.append(buffer);
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
reader.close();
|
||||
|
||||
return sb.toString();
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "Failed to read file, FileNotFoundException: " + e.getMessage());
|
||||
return null;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(TAG, "Failed to read file, UnsupportedEncodingException: " + e.getMessage());
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to read file, IOException: " + e.getMessage());
|
||||
return null;
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to close stream, IOException: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPostExecute(final String result) {
|
||||
if (result != null) {
|
||||
this.callback.onComplete(result, this.context);
|
||||
} else {
|
||||
this.callback.onError("ReadTextTask returns no result.", this.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ReadTask {
|
||||
private CompleteCallback callback;
|
||||
private Object context;
|
||||
|
||||
public ReadTask(CompleteCallback callback, Object context) {
|
||||
this.callback = callback;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected byte[] doInBackground(String... params) {
|
||||
java.io.File javaFile = new java.io.File(params[0]);
|
||||
FileInputStream stream = null;
|
||||
|
||||
try {
|
||||
stream = new FileInputStream(javaFile);
|
||||
|
||||
byte[] result = new byte[(int)javaFile.length()];
|
||||
|
||||
DataInputStream dataInputStream = new DataInputStream(stream);
|
||||
dataInputStream.readFully(result);
|
||||
|
||||
return result;
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "Failed to read file, FileNotFoundException: " + e.getMessage());
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to read file, IOException: " + e.getMessage());
|
||||
return null;
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to close stream, IOException: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPostExecute(final byte[] result) {
|
||||
if (result != null) {
|
||||
this.callback.onComplete(result, this.context);
|
||||
} else {
|
||||
this.callback.onError("ReadTask returns no result.", this.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class WriteTextTask {
|
||||
private CompleteCallback callback;
|
||||
private Object context;
|
||||
|
||||
public WriteTextTask(CompleteCallback callback, Object context) {
|
||||
this.callback = callback;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected boolean doInBackground(String... params) {
|
||||
java.io.File javaFile = new java.io.File(params[0]);
|
||||
FileOutputStream stream = null;
|
||||
try {
|
||||
stream = new FileOutputStream(javaFile);
|
||||
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream, params[2]);
|
||||
|
||||
writer.write(params[1]);
|
||||
writer.close();
|
||||
|
||||
return true;
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "Failed to write file, FileNotFoundException: " + e.getMessage());
|
||||
return false;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(TAG, "Failed to write file, UnsupportedEncodingException: " + e.getMessage());
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to write file, IOException: " + e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to close stream, IOException: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPostExecute(final boolean result) {
|
||||
if (result) {
|
||||
this.callback.onComplete(null, this.context);
|
||||
} else {
|
||||
this.callback.onError("WriteTextTask returns no result.", this.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class WriteTask {
|
||||
private CompleteCallback callback;
|
||||
private Object context;
|
||||
|
||||
public WriteTask(CompleteCallback callback, Object context) {
|
||||
this.callback = callback;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected boolean doInBackground(Object... params) {
|
||||
java.io.File javaFile = new java.io.File((String)params[0]);
|
||||
FileOutputStream stream = null;
|
||||
byte[] content = (byte[])params[1];
|
||||
|
||||
try {
|
||||
stream = new FileOutputStream(javaFile);
|
||||
stream.write(content, 0, content.length);
|
||||
|
||||
return true;
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "Failed to write file, FileNotFoundException: " + e.getMessage());
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to write file, IOException: " + e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to close stream, IOException: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPostExecute(final boolean result) {
|
||||
if (result) {
|
||||
this.callback.onComplete(null, this.context);
|
||||
} else {
|
||||
this.callback.onError("WriteTask returns no result.", this.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,10 @@
|
||||
B8E76F5B212C2F4E009CFCE2 /* UIView+PropertyBag.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E76F59212C2F4E009CFCE2 /* UIView+PropertyBag.m */; };
|
||||
B8E76F5E212C3134009CFCE2 /* UIView+PassThroughParent.h in Headers */ = {isa = PBXBuildFile; fileRef = B8E76F5C212C3134009CFCE2 /* UIView+PassThroughParent.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B8E76F5F212C3134009CFCE2 /* UIView+PassThroughParent.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E76F5D212C3134009CFCE2 /* UIView+PassThroughParent.m */; };
|
||||
D004030F22F781A50089EAD8 /* NSString+Async.h in Headers */ = {isa = PBXBuildFile; fileRef = D004030D22F781A50089EAD8 /* NSString+Async.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D004031022F781A50089EAD8 /* NSString+Async.m in Sources */ = {isa = PBXBuildFile; fileRef = D004030E22F781A50089EAD8 /* NSString+Async.m */; };
|
||||
D004031322FA27D60089EAD8 /* NSData+Async.h in Headers */ = {isa = PBXBuildFile; fileRef = D004031122FA27D60089EAD8 /* NSData+Async.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D004031422FA27D60089EAD8 /* NSData+Async.m in Sources */ = {isa = PBXBuildFile; fileRef = D004031222FA27D60089EAD8 /* NSData+Async.m */; };
|
||||
F915D3551EC9EF5E00071914 /* TNSProcess.m in Sources */ = {isa = PBXBuildFile; fileRef = F915D3531EC9EF5E00071914 /* TNSProcess.m */; };
|
||||
F915D3561EC9EF5E00071914 /* TNSProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = F915D3541EC9EF5E00071914 /* TNSProcess.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F98F5CB31CD0EFEA00978308 /* TNSWidgets.h in Headers */ = {isa = PBXBuildFile; fileRef = F98F5CB21CD0EFEA00978308 /* TNSWidgets.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@ -43,6 +47,10 @@
|
||||
B8E76F59212C2F4E009CFCE2 /* UIView+PropertyBag.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+PropertyBag.m"; sourceTree = "<group>"; };
|
||||
B8E76F5C212C3134009CFCE2 /* UIView+PassThroughParent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+PassThroughParent.h"; sourceTree = "<group>"; };
|
||||
B8E76F5D212C3134009CFCE2 /* UIView+PassThroughParent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+PassThroughParent.m"; sourceTree = "<group>"; };
|
||||
D004030D22F781A50089EAD8 /* NSString+Async.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+Async.h"; sourceTree = "<group>"; };
|
||||
D004030E22F781A50089EAD8 /* NSString+Async.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+Async.m"; sourceTree = "<group>"; };
|
||||
D004031122FA27D60089EAD8 /* NSData+Async.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSData+Async.h"; sourceTree = "<group>"; };
|
||||
D004031222FA27D60089EAD8 /* NSData+Async.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSData+Async.m"; sourceTree = "<group>"; };
|
||||
F915D3531EC9EF5E00071914 /* TNSProcess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TNSProcess.m; path = ../TNSProcess.m; sourceTree = "<group>"; };
|
||||
F915D3541EC9EF5E00071914 /* TNSProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TNSProcess.h; path = ../TNSProcess.h; sourceTree = "<group>"; };
|
||||
F98F5CAF1CD0EFEA00978308 /* TNSWidgets.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TNSWidgets.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -100,6 +108,8 @@
|
||||
F98F5CB21CD0EFEA00978308 /* TNSWidgets.h */,
|
||||
8B7321CD1D097ECD00884AC6 /* TNSLabel.h */,
|
||||
8B7321CE1D097ECD00884AC6 /* TNSLabel.m */,
|
||||
D004030D22F781A50089EAD8 /* NSString+Async.h */,
|
||||
D004030E22F781A50089EAD8 /* NSString+Async.m */,
|
||||
F98F5CC91CD0F09E00978308 /* UIImage+TNSBlocks.h */,
|
||||
F98F5CCA1CD0F09E00978308 /* UIImage+TNSBlocks.m */,
|
||||
F98F5CB41CD0EFEA00978308 /* Info.plist */,
|
||||
@ -109,6 +119,8 @@
|
||||
B8E76F59212C2F4E009CFCE2 /* UIView+PropertyBag.m */,
|
||||
B8E76F5C212C3134009CFCE2 /* UIView+PassThroughParent.h */,
|
||||
B8E76F5D212C3134009CFCE2 /* UIView+PassThroughParent.m */,
|
||||
D004031122FA27D60089EAD8 /* NSData+Async.h */,
|
||||
D004031222FA27D60089EAD8 /* NSData+Async.m */,
|
||||
);
|
||||
path = TNSWidgets;
|
||||
sourceTree = "<group>";
|
||||
@ -132,9 +144,11 @@
|
||||
F915D3561EC9EF5E00071914 /* TNSProcess.h in Headers */,
|
||||
F98F5CB31CD0EFEA00978308 /* TNSWidgets.h in Headers */,
|
||||
B8E76F5E212C3134009CFCE2 /* UIView+PassThroughParent.h in Headers */,
|
||||
D004031322FA27D60089EAD8 /* NSData+Async.h in Headers */,
|
||||
F98F5CCB1CD0F09E00978308 /* UIImage+TNSBlocks.h in Headers */,
|
||||
B8E76F52212C2DA2009CFCE2 /* NSObject+Swizzling.h in Headers */,
|
||||
B8E76F5A212C2F4E009CFCE2 /* UIView+PropertyBag.h in Headers */,
|
||||
D004030F22F781A50089EAD8 /* NSString+Async.h in Headers */,
|
||||
8B7321CF1D097ECD00884AC6 /* TNSLabel.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -200,6 +214,7 @@
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = F98F5CA51CD0EFEA00978308;
|
||||
@ -239,6 +254,8 @@
|
||||
F915D3551EC9EF5E00071914 /* TNSProcess.m in Sources */,
|
||||
F98F5CCC1CD0F09E00978308 /* UIImage+TNSBlocks.m in Sources */,
|
||||
B8E76F53212C2DA2009CFCE2 /* NSObject+Swizzling.m in Sources */,
|
||||
D004031022F781A50089EAD8 /* NSString+Async.m in Sources */,
|
||||
D004031422FA27D60089EAD8 /* NSData+Async.m in Sources */,
|
||||
B8E76F5B212C2F4E009CFCE2 /* UIView+PropertyBag.m in Sources */,
|
||||
B8E76F5F212C3134009CFCE2 /* UIView+PassThroughParent.m in Sources */,
|
||||
);
|
||||
|
@ -0,0 +1,24 @@
|
||||
//
|
||||
// NSData+Async.h
|
||||
// TNSWidgets
|
||||
//
|
||||
// Created by Peter Staev on 7.08.19.
|
||||
// Copyright © 2019 Telerik A D. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSData (Async)
|
||||
|
||||
+ (void)dataWithContentsOfFile:(nonnull NSString*)path
|
||||
completion:(void (^) (NSData*))callback;
|
||||
|
||||
- (void)writeToFile:(nonnull NSString*) path
|
||||
atomically:(BOOL)atomically
|
||||
completion:(void (^) ())callback;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,42 @@
|
||||
//
|
||||
// NSData+Async.m
|
||||
// TNSWidgets
|
||||
//
|
||||
// Created by Peter Staev on 7.08.19.
|
||||
// Copyright © 2019 Telerik A D. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSData+Async.h"
|
||||
|
||||
@implementation NSData (Async)
|
||||
|
||||
+ (void)dataWithContentsOfFile:(nonnull NSString*)path
|
||||
completion:(void (^) (NSData*))callback {
|
||||
|
||||
dispatch_queue_t asyncQueue = dispatch_queue_create("org.nativescript.TNSWidgets.data", NULL);
|
||||
dispatch_async(asyncQueue, ^(void) {
|
||||
NSData *output = [NSData dataWithContentsOfFile:path];
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
callback(output);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
- (void)writeToFile:(nonnull NSString*) path
|
||||
atomically:(BOOL)atomically
|
||||
completion:(void (^) ())callback {
|
||||
|
||||
dispatch_queue_t asyncQueue = dispatch_queue_create("org.nativescript.TNSWidgets.data", NULL);
|
||||
dispatch_async(asyncQueue, ^(void) {
|
||||
[self writeToFile:path
|
||||
atomically:atomically];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
callback();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,25 @@
|
||||
//
|
||||
// NSString+Async.h
|
||||
// TNSWidgets
|
||||
//
|
||||
// Created by Peter Staev on 5.08.19.
|
||||
// Copyright © 2019 Telerik A D. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSString (Async)
|
||||
|
||||
+ (void)stringWithContentsOfFile:(nonnull NSString*)path
|
||||
encoding:(NSStringEncoding)enc
|
||||
completion:(void (^) (NSString*, NSError*))callback;
|
||||
|
||||
- (void)writeToFile:(nonnull NSString*) path
|
||||
atomically:(BOOL)atomically
|
||||
encoding:(NSStringEncoding)enc
|
||||
completion:(void (^) (NSError*))callback;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,50 @@
|
||||
//
|
||||
// NSString+Async.m
|
||||
// TNSWidgets
|
||||
//
|
||||
// Created by Peter Staev on 5.08.19.
|
||||
// Copyright © 2019 Telerik A D. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSString+Async.h"
|
||||
|
||||
@implementation NSString (Async)
|
||||
|
||||
+ (void)stringWithContentsOfFile:(nonnull NSString*)path
|
||||
encoding:(NSStringEncoding)enc
|
||||
completion:(void (^) (NSString*, NSError*))callback {
|
||||
|
||||
dispatch_queue_t asyncQueue = dispatch_queue_create("org.nativescript.TNSWidgets.string", NULL);
|
||||
dispatch_async(asyncQueue, ^(void) {
|
||||
NSError *error = nil;
|
||||
NSString *output = [NSString stringWithContentsOfFile:path
|
||||
encoding:enc
|
||||
error:&error];
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
callback(output, error);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
- (void)writeToFile:(nonnull NSString*)path
|
||||
atomically:(BOOL)atomically
|
||||
encoding:(NSStringEncoding)enc
|
||||
completion:(void (^) (NSError*))callback {
|
||||
|
||||
dispatch_queue_t asyncQueue = dispatch_queue_create("org.nativescript.TNSWidgets.string", NULL);
|
||||
dispatch_async(asyncQueue, ^(void) {
|
||||
NSError *error = nil;
|
||||
[self writeToFile:path
|
||||
atomically:atomically
|
||||
encoding:enc
|
||||
error:&error];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
callback(error);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
@ -20,3 +20,5 @@ FOUNDATION_EXPORT const unsigned char TNSWidgetsVersionString[];
|
||||
#import "UIView+PassThroughParent.h"
|
||||
#import "TNSLabel.h"
|
||||
#import "TNSProcess.h"
|
||||
#import "NSString+Async.h"
|
||||
#import "NSData+Async.h"
|
||||
|
@ -218,7 +218,30 @@ export class FileSystemAccess {
|
||||
return this.getLogicalRootPath() + "/app";
|
||||
}
|
||||
|
||||
public read(path: string, onError?: (error: any) => any) {
|
||||
public read = this.readSync.bind(this);
|
||||
|
||||
public readAsync(path: string): Promise<number[]> {
|
||||
return new Promise<number[]>((resolve, reject) => {
|
||||
try {
|
||||
org.nativescript.widgets.Async.File.read(
|
||||
path,
|
||||
new org.nativescript.widgets.Async.CompleteCallback({
|
||||
onComplete: (result: number[]) => {
|
||||
resolve(result);
|
||||
},
|
||||
onError: (err) => {
|
||||
reject(new Error(err));
|
||||
}
|
||||
}),
|
||||
null,
|
||||
);
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public readSync(path: string, onError?: (error: any) => any) {
|
||||
try {
|
||||
const javaFile = new java.io.File(path);
|
||||
const stream = new java.io.FileInputStream(javaFile);
|
||||
@ -234,7 +257,31 @@ export class FileSystemAccess {
|
||||
}
|
||||
}
|
||||
|
||||
public write(path: string, bytes: native.Array<number>, onError?: (error: any) => any) {
|
||||
public write = this.writeSync.bind(this);
|
||||
|
||||
public writeAsync(path: string, bytes: native.Array<number>): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
org.nativescript.widgets.Async.File.write(
|
||||
path,
|
||||
bytes,
|
||||
new org.nativescript.widgets.Async.CompleteCallback({
|
||||
onComplete: () => {
|
||||
resolve();
|
||||
},
|
||||
onError: (err) => {
|
||||
reject(new Error(err));
|
||||
}
|
||||
}),
|
||||
null,
|
||||
);
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public writeSync(path: string, bytes: native.Array<number>, onError?: (error: any) => any) {
|
||||
try {
|
||||
const javaFile = new java.io.File(path);
|
||||
const stream = new java.io.FileOutputStream(javaFile);
|
||||
@ -247,7 +294,40 @@ export class FileSystemAccess {
|
||||
}
|
||||
}
|
||||
|
||||
public readText(path: string, onError?: (error: any) => any, encoding?: any) {
|
||||
public readText = this.readTextSync.bind(this);
|
||||
|
||||
public readTextAsync(path: string, encoding?: any): Promise<string> {
|
||||
let actualEncoding = encoding;
|
||||
if (!actualEncoding) {
|
||||
actualEncoding = textModule.encoding.UTF_8;
|
||||
}
|
||||
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
try {
|
||||
org.nativescript.widgets.Async.File.readText(
|
||||
path,
|
||||
actualEncoding,
|
||||
new org.nativescript.widgets.Async.CompleteCallback({
|
||||
onComplete: (result: string) => {
|
||||
if (actualEncoding === textModule.encoding.UTF_8) {
|
||||
// Remove UTF8 BOM if present. http://www.rgagnon.com/javadetails/java-handle-utf8-file-with-bom.html
|
||||
result = FileSystemAccess._removeUtf8Bom(result);
|
||||
}
|
||||
resolve(result);
|
||||
},
|
||||
onError: (err) => {
|
||||
reject(new Error(err));
|
||||
}
|
||||
}),
|
||||
null,
|
||||
);
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public readTextSync(path: string, onError?: (error: any) => any, encoding?: any) {
|
||||
try {
|
||||
const javaFile = new java.io.File(path);
|
||||
const stream = new java.io.FileInputStream(javaFile);
|
||||
@ -302,7 +382,37 @@ export class FileSystemAccess {
|
||||
return s;
|
||||
}
|
||||
|
||||
public writeText(path: string, content: string, onError?: (error: any) => any, encoding?: any) {
|
||||
public writeText = this.writeTextSync.bind(this);
|
||||
|
||||
public writeTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
||||
let actualEncoding = encoding;
|
||||
if (!actualEncoding) {
|
||||
actualEncoding = textModule.encoding.UTF_8;
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
org.nativescript.widgets.Async.File.writeText(
|
||||
path,
|
||||
content,
|
||||
actualEncoding,
|
||||
new org.nativescript.widgets.Async.CompleteCallback({
|
||||
onComplete: () => {
|
||||
resolve();
|
||||
},
|
||||
onError: (err) => {
|
||||
reject(new Error(err));
|
||||
}
|
||||
}),
|
||||
null,
|
||||
);
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public writeTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any) {
|
||||
try {
|
||||
const javaFile = new java.io.File(path);
|
||||
const stream = new java.io.FileOutputStream(javaFile);
|
||||
|
@ -130,6 +130,23 @@ export class FileSystemAccess {
|
||||
*/
|
||||
readText(path: string, onError?: (error: any) => any, encoding?: any): string;
|
||||
|
||||
/**
|
||||
* Reads a text from a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
* @param encoding (optional) If set reads the text with the specified encoding (default UTF-8).
|
||||
* Returns Promise of the text read.
|
||||
*/
|
||||
readTextAsync(path: string, encoding?: any): Promise<string>;
|
||||
|
||||
/**
|
||||
* Reads a text from a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
* @param onError (optional) A callback function to use if any error occurs.
|
||||
* @param encoding (optional) If set reads the text with the specified encoding (default UTF-8).
|
||||
* Returns the text read.
|
||||
*/
|
||||
readTextSync(path: string, onError?: (error: any) => any, encoding?: any): string;
|
||||
|
||||
/**
|
||||
* Reads a binary content from a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
@ -138,6 +155,21 @@ export class FileSystemAccess {
|
||||
*/
|
||||
read(path: string, onError?: (error: any) => any): any;
|
||||
|
||||
/**
|
||||
* Reads a binary content from a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
* Returns a Promise with the binary content read.
|
||||
*/
|
||||
readAsync(path: string): Promise<any>;
|
||||
|
||||
/**
|
||||
* Reads a binary content from a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
* @param onError (optional) A callback function to use if any error occurs.
|
||||
* Returns the binary content read.
|
||||
*/
|
||||
readSync(path: string, onError?: (error: any) => any): any;
|
||||
|
||||
/**
|
||||
* Writes a text to a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
@ -147,6 +179,23 @@ export class FileSystemAccess {
|
||||
*/
|
||||
writeText(path: string, content: string, onError?: (error: any) => any, encoding?: any);
|
||||
|
||||
/**
|
||||
* Writes a text to a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
* @param content The content which will be written to the file.
|
||||
* @param encoding (optional) If set writes the text with the specified encoding (default UTF-8).
|
||||
*/
|
||||
writeTextAsync(path: string, content: string, encoding?: any): Promise<void>;
|
||||
|
||||
/**
|
||||
* Writes a text to a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
* @param content The content which will be written to the file.
|
||||
* @param onError (optional) A callback function to use if any error occurs.
|
||||
* @param encoding (optional) If set writes the text with the specified encoding (default UTF-8).
|
||||
*/
|
||||
writeTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any);
|
||||
|
||||
/**
|
||||
* Writes a binary to a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
@ -155,6 +204,21 @@ export class FileSystemAccess {
|
||||
*/
|
||||
write(path: string, content: any, onError?: (error: any) => any);
|
||||
|
||||
/**
|
||||
* Writes a binary to a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
* @param content The content which will be written to the file.
|
||||
*/
|
||||
writeAsync(path: string, content: any): Promise<void>;
|
||||
|
||||
/**
|
||||
* Writes a binary to a file with a given path.
|
||||
* @param path The path to the source file.
|
||||
* @param content The content which will be written to the file.
|
||||
* @param onError (optional) A callback function to use if any error occurs.
|
||||
*/
|
||||
writeSync(path: string, content: any, onError?: (error: any) => any);
|
||||
|
||||
/**
|
||||
* Gets extension of the file with a given path.
|
||||
* @param path A path to the file.
|
||||
|
@ -257,7 +257,31 @@ export class FileSystemAccess {
|
||||
return ios.getCurrentAppPath();
|
||||
}
|
||||
|
||||
public readText(path: string, onError?: (error: any) => any, encoding?: any) {
|
||||
public readText = this.readTextSync.bind(this);
|
||||
|
||||
public readTextAsync(path: string, encoding?: any) {
|
||||
const actualEncoding = encoding || textEncoding.UTF_8;
|
||||
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
try {
|
||||
(NSString as any).stringWithContentsOfFileEncodingCompletion(
|
||||
path,
|
||||
actualEncoding,
|
||||
(result, error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (ex) {
|
||||
reject(new Error("Failed to read file at path '" + path + "': " + ex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public readTextSync(path: string, onError?: (error: any) => any, encoding?: any) {
|
||||
const actualEncoding = encoding || textEncoding.UTF_8;
|
||||
|
||||
try {
|
||||
@ -271,7 +295,19 @@ export class FileSystemAccess {
|
||||
}
|
||||
}
|
||||
|
||||
public read(path: string, onError?: (error: any) => any): NSData {
|
||||
public read = this.readSync.bind(this);
|
||||
|
||||
public readAsync(path: string): Promise<NSData> {
|
||||
return new Promise<NSData>((resolve, reject) => {
|
||||
try {
|
||||
(NSData as any).dataWithContentsOfFileCompletion(path, resolve);
|
||||
} catch (ex) {
|
||||
reject(new Error("Failed to read file at path '" + path + "': " + ex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public readSync(path: string, onError?: (error: any) => any): NSData {
|
||||
try {
|
||||
return NSData.dataWithContentsOfFile(path);
|
||||
} catch (ex) {
|
||||
@ -281,7 +317,33 @@ export class FileSystemAccess {
|
||||
}
|
||||
}
|
||||
|
||||
public writeText(path: string, content: string, onError?: (error: any) => any, encoding?: any) {
|
||||
public writeText = this.writeTextSync.bind(this);
|
||||
|
||||
public writeTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
||||
const nsString = NSString.stringWithString(content);
|
||||
const actualEncoding = encoding || textEncoding.UTF_8;
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
(nsString as any).writeToFileAtomicallyEncodingCompletion(
|
||||
path,
|
||||
true,
|
||||
actualEncoding,
|
||||
(error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (ex) {
|
||||
reject(new Error("Failed to write file at path '" + path + "': " + ex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public writeTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any) {
|
||||
const nsString = NSString.stringWithString(content);
|
||||
|
||||
const actualEncoding = encoding || textEncoding.UTF_8;
|
||||
@ -296,7 +358,23 @@ export class FileSystemAccess {
|
||||
}
|
||||
}
|
||||
|
||||
public write(path: string, content: NSData, onError?: (error: any) => any) {
|
||||
public write = this.writeSync.bind(this);
|
||||
|
||||
public writeAsync(path: string, content: NSData): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
(content as any).writeToFileAtomicallyCompletion(
|
||||
path,
|
||||
true,
|
||||
() => { resolve(); },
|
||||
);
|
||||
} catch (ex) {
|
||||
reject(new Error("Failed to write file at path '" + path + "': " + ex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public writeSync(path: string, content: NSData, onError?: (error: any) => any) {
|
||||
try {
|
||||
content.writeToFileAtomically(path, true);
|
||||
} catch (ex) {
|
||||
|
11
tns-core-modules/file-system/file-system.d.ts
vendored
11
tns-core-modules/file-system/file-system.d.ts
vendored
@ -96,6 +96,11 @@ export class File extends FileSystemEntity {
|
||||
*/
|
||||
readTextSync(onError?: (error: any) => any, encoding?: string): string;
|
||||
|
||||
/**
|
||||
* Reads the binary content of the file asynchronously.
|
||||
*/
|
||||
read(): Promise<any>;
|
||||
|
||||
/**
|
||||
* Reads the binary content of the file synchronously.
|
||||
* @param onError An optional function to be called if some IO-error occurs.
|
||||
@ -117,6 +122,12 @@ export class File extends FileSystemEntity {
|
||||
*/
|
||||
writeTextSync(content: string, onError?: (error: any) => any, encoding?: string): void;
|
||||
|
||||
/**
|
||||
* Writes the provided binary content to the file.
|
||||
* @param content The binary content to be saved to the file.
|
||||
*/
|
||||
write(content: any): Promise<void>;
|
||||
|
||||
/**
|
||||
* Writes the provided binary content to the file synchronously.
|
||||
* @param content The binary content to be saved to the file.
|
||||
|
@ -207,6 +207,31 @@ export class File extends FileSystemEntity {
|
||||
return getFileAccess().getFileSize(this.path);
|
||||
}
|
||||
|
||||
public read(): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
try {
|
||||
this.checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._locked = true;
|
||||
|
||||
getFileAccess().readAsync(this.path).then(
|
||||
(result) => {
|
||||
resolve(result);
|
||||
this._locked = false;
|
||||
},
|
||||
(error) => {
|
||||
reject(error);
|
||||
this._locked = false;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public readSync(onError?: (error: any) => any): any {
|
||||
this.checkAccess();
|
||||
|
||||
@ -220,7 +245,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
};
|
||||
|
||||
const content = getFileAccess().read(this.path, localError);
|
||||
const content = getFileAccess().readSync(this.path, localError);
|
||||
|
||||
this._locked = false;
|
||||
|
||||
@ -228,6 +253,31 @@ export class File extends FileSystemEntity {
|
||||
|
||||
}
|
||||
|
||||
public write(content: any): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
this.checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._locked = true;
|
||||
|
||||
getFileAccess().writeAsync(this.path, content).then(
|
||||
() => {
|
||||
resolve();
|
||||
this._locked = false;
|
||||
},
|
||||
(error) => {
|
||||
reject(error);
|
||||
this._locked = false;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public writeSync(content: any, onError?: (error: any) => any): void {
|
||||
this.checkAccess();
|
||||
|
||||
@ -242,7 +292,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
};
|
||||
|
||||
getFileAccess().write(this.path, content, localError);
|
||||
getFileAccess().writeSync(this.path, content, localError);
|
||||
} finally {
|
||||
this._locked = false;
|
||||
}
|
||||
@ -250,16 +300,26 @@ export class File extends FileSystemEntity {
|
||||
|
||||
public readText(encoding?: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let hasError = false;
|
||||
const localError = (error) => {
|
||||
hasError = true;
|
||||
reject(error);
|
||||
};
|
||||
try {
|
||||
this.checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
const content = this.readTextSync(localError, encoding);
|
||||
if (!hasError) {
|
||||
resolve(content);
|
||||
return;
|
||||
}
|
||||
|
||||
this._locked = true;
|
||||
|
||||
getFileAccess().readTextAsync(this.path, encoding).then(
|
||||
(result) => {
|
||||
resolve(result);
|
||||
this._locked = false;
|
||||
},
|
||||
(error) => {
|
||||
reject(error);
|
||||
this._locked = false;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -277,7 +337,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
};
|
||||
|
||||
const content = getFileAccess().readText(this.path, localError, encoding);
|
||||
const content = getFileAccess().readTextSync(this.path, localError, encoding);
|
||||
this._locked = false;
|
||||
|
||||
return content;
|
||||
@ -285,16 +345,26 @@ export class File extends FileSystemEntity {
|
||||
|
||||
public writeText(content: string, encoding?: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let hasError = false;
|
||||
const localError = function (error) {
|
||||
hasError = true;
|
||||
reject(error);
|
||||
};
|
||||
try {
|
||||
this.checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
this.writeTextSync(content, localError, encoding);
|
||||
if (!hasError) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
this._locked = true;
|
||||
|
||||
getFileAccess().writeTextAsync(this.path, content, encoding).then(
|
||||
() => {
|
||||
resolve();
|
||||
this._locked = false;
|
||||
},
|
||||
(error) => {
|
||||
reject(error);
|
||||
this._locked = false;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -312,8 +382,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Asyncronous
|
||||
getFileAccess().writeText(this.path, content, localError, encoding);
|
||||
getFileAccess().writeTextSync(this.path, content, localError, encoding);
|
||||
} finally {
|
||||
this._locked = false;
|
||||
}
|
||||
|
@ -17,6 +17,13 @@
|
||||
export function download(url: string, callback: CompleteCallback, context: any);
|
||||
}
|
||||
|
||||
export module File {
|
||||
export function readText(path: string, encoding: string, callback: CompleteCallback, context: any);
|
||||
export function read(path: string, callback: CompleteCallback, context: any);
|
||||
export function writeText(path: string, content: string, encoding: string, callback: CompleteCallback, context: any);
|
||||
export function write(path: string, content: native.Array<number>, callback: CompleteCallback, context: any);
|
||||
}
|
||||
|
||||
export module Http {
|
||||
export class KeyValuePair {
|
||||
public key: string;
|
||||
|
Reference in New Issue
Block a user