Android bindings to Storj V3 libuplink.
Add the Gradle dependency to the build.gradle
file of the app module:
dependencies {
implementation 'io.storj:libuplink-android:0.12.0'
}
The ApiKey
contains the access key to a project on the satellite.
API keys are obtained from the satellite Web interface as a serialized string. They must be parsed to ApiKey
object.
String serializedApiKey = "13Yqft7v...";
ApiKey apiKey = ApiKey.parse(serializedApiKey);
The Scope
contains all information required to access resources on the Storj network:
New Scope
can be created from Satellite Address, API Key and passphrase.
String satelliteAddress = "us-central-1.tardigrade.io:7777";
String serializedApiKey = "13Yqft7v...";
String passphrase = "super secret passphrase";
ApiKey apiKey = ApiKey.parse(serializedApiKey);
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(satelliteAddress, apiKey)) {
Key saltedKey = Key.getSaltedKeyFromPassphrase(project, passphrase);
EncryptionAccess access = new EncryptionAccess(saltedKey);
Scope scope = new Scope(satelliteAddress, apiKey, access);
}
Scopes can be restricted in terms of allowed operations and access to specific buckets and objects.
Scope sharedScope = SCOPE.restrict(
new Caveat.Builder().disallowDeletes(true).disallowWrites(true).build(),
new EncryptionRestriction("my-bucket", "pictures/birthday/"));
Scopes can be serialized to string for easy sharing.
Scope scope = ...;
String serializedScope = scope.serialize();
If received a serialized Scope as a string, it can be parsed to Scope
object.
String serializedScope = "13GRuHAW...";
Scope scope = Scope.parse(serializedScope);
Scope scope = ...;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope)) {
project.createBucket("my-bucket"));
}
Scope scope = ...;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope)) {
BucketInfo info = project.getBucketInfo("my-bucket"));
}
Scope scope = ...;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope)) {
Iterable<BucketInfo> buckets = project.listBucket();
for (BucketInfo bucket : buckets) {
// Do something for each bucket.
}
}
Scope scope = ...;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope)) {
project.deleteBucket("my-bucket"));
}
Below is the easiest way for downloading a complete object to a local file. bucket.downloadObject
is a blocking operation. It will return when the download is complete.
Scope scope = ...;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope);
Bucket bucket = project.openBucket("my-bucket", scope);
OutputStream out = new FileOutputStream("path/to/local/file")) {
bucket.downloadObject("path/to/my/object", out);
}
If only a portion of the object should be downloaded, this can be specified with the off
and len
parameters. The example below will download only 4 KiB from the object, starting at 1 KiB offset.
Scope scope = ...;
long off = 1024;
long len = 4096;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope);
Bucket bucket = project.openBucket("my-bucket", scope);
OutputStream out = new FileOutputStream("path/to/local/file")) {
bucket.downloadObject("path/to/my/object", out, off, len);
}
If progress monitoring and/or cancellation is important, the client can take advantage of the ObjectInputStream
class.
As all operations in the Storj Java API are blocking, the client should use some means for asynchronous processing - like the AsyncTask
from the Android platform.
The example below shows how to download with progress monitoring and cancellation using the AsyncTask
:
public class DownloadTask extends AsyncTask<Void, Long, Throwable> {
private Scope mScope;
private ObjectInfo mFile;
private File mLocalFile;
private int mNotificationId;
private long mDownloadedBytes;
private long mLastNotifiedTime;
private ObjectInputStream mInputStream;
DownloadTask(Scope scope, ObjectInfo file, File localFile) {
mScope scope;
mFile = file;
mLocalFile = localFile;
}
@Override
protected Exception doInBackground(Void... params) {
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(mScope);
Bucket bucket = project.openBucket(mFile.getBucket(), mScope);
ObjectInputStream in = new ObjectInputStream(bucket, mFile.getPath());
OutputStream out = new FileOutputStream(mLocalFile)) {
mInputStream = in;
byte[] buffer = new byte[128 * 1024];
int len;
while ((len = in.read(buffer)) != -1) {
if (isCancelled()) {
in.cancel();
return null;
}
out.write(buffer, 0, len);
if (isCancelled()) {
in.cancel();
return null;
}
publishProgress((long) len);
}
} catch (StorjException | IOException e) {
return e;
}
return null;
}
@Override
protected void onProgressUpdate(Long... params) {
long increment = params[0];
mDownloadedBytes += increment;
long now = System.currentTimeMillis();
// Calculate the progress in percents.
int progress = (int) ((mDownloadedBytes * 100) / mFile.getSize());
// Check if 1 second elapsed since last notification or progress is at 100%.
if (progress == 100 || mLastNotifiedTime == 0 || now > mLastNotifiedTime + 1150) {
// Place your code here to update the GUI with the new progress.
mLastNotifiedTime = now;
}
}
@Override
protected void onPostExecute(Throwable t) {
if (t != null) {
String errorMessage = t.getMessage();
// The download failed.
// Place your code here to update the GUI with the error message.
return;
}
// The download is successful.
// Place your code here to update the GUI.
}
protected void onCancelled(Throwable t) {
// The download was cancelled.
// Place your code here to update the GUI.
}
/**
* Call this method to cancel the download.
*/
void cancel() {
this.cancel(false);
mInputStream.cancel();
}
}
Below is the easiest way for uploading new object. bucket.uploadObject
is a blocking operation. It will return when the upload is complete.
Note the importance of specifying the location of the temp directory using the UplinkOption.tempDir()
option. This is where the file being uploaded will be first encrypted before actually uploaded into pieces to the Storj network. For Android, it is recommended to set the temp directory to the application’s cache directory.
Scope scope = ...;
String tempDir = mActivity.getCacheDir().getPath();
try (Uplink uplink = new Uplink(UplinkOption.tempDir(tempDir));
Project project = uplink.openProject(scope);
Bucket bucket = project.openBucket("my-bucket", scope);
InputStream in = new FileInputStream("path/to/local/file")) {
bucket.uploadObject("path/to/my/object", in);
}
If progress monitoring and/or cancellation is important, the client can take advantage of the ObjectOutputStream
class.
As all operations in the Storj Java API are blocking, the client should use some means for asynchronous processing - like the AsyncTask
from the Android platform.
The example below shows how to upload with progress monitoring and cancellation using the AsyncTask
.
Note the importance of specifying the location of the temp directory using the UplinkOption.tempDir()
option. This is where the file being uploaded will be first encrypted before actually uploaded into pieces to the Storj network. For Android, it is recommended to set the temp directory to the application’s cache directory.
public class UploadTask extends AsyncTask<Void, Long, Throwable> {
private Scope mScope;
private String mBucket;
private String objectPath;
private File mFile;
private String mTempDir;
private long mFileSize;
private long mUploadedBytes;
private long mLastNotifiedTime;
private ObjectOutputStream mOutputStream;
UploadTask(Scope scope, String bucket, String objectPath, File file, String tempDir) {
mScope = scope;
mBucket = bucket;
mObjectPath = objectPath;
mFile = file;
mTempDir = tempDir;
mFileSize = mFile.length();
}
@Override
protected Exception doInBackground(Void... params) {
try (Uplink uplink = new Uplink(UplinkOption.tempDir(mTempDir));
Project project = uplink.openProject(mScope);
Bucket bucket = project.openBucket(mBucket, mScope);
InputStream in = new FileInputStream(mFile.getPath());
ObjectOutputStream out = new ObjectOutputStream(bucket, mObjectPath)) {
mOutputStream = out;
byte[] buffer = new byte[128 * 1024];
int len;
while ((len = in.read(buffer)) != -1) {
if (isCancelled()) {
out.cancel();
return null;
}
out.write(buffer, 0, len);
if (isCancelled()) {
out.cancel();
return null;
}
publishProgress((long) len);
}
} catch (StorjException | IOException e) {
return e;
}
return null;
}
@Override
protected void onProgressUpdate(Long... params) {
long increment = params[0];
mUploadedBytes += increment;
long now = System.currentTimeMillis();
// Calculate the progress in percents.
int progress = (int) ((mUploadedBytes * 100) / mFileSize);
// Check if 1 second elapsed since last notification or progress is at 100%.
if (progress == 100 || mLastNotifiedTime == 0 || now > mLastNotifiedTime + 1150) {
// Place your code here to update the GUI with the new progress.
mLastNotifiedTime = now;
}
}
@Override
protected void onPostExecute(Throwable t) {
if (t != null) {
String errorMessage = t.getMessage();
// The upload failed.
// Place your code here to update the GUI with the error message.
return;
}
// The upload is successful.
// Place your code here to update the GUI.
}
protected void onCancelled(Throwable t) {
// The upload was cancelled.
// Place your code here to update the GUI.
}
/**
* Call this method to cancel the upload.
*/
void cancel() {
this.cancel(false);
mOutputStream.cancel();
}
}
Listing the content of a bucket, non-recursive:
Scope scope = ...;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope);
Bucket bucket = project.openBucket("my-bucket", scope)) {
Iterable<ObjectInfo> objects = bucket.listObjects();
for (ObjectInfo object : objects) {
// Do something for each object.
}
}
Listing all content under specific prefix, recursive:
Scope scope = ...;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope);
Bucket bucket = project.openBucket("my-bucket", scope)) {
Iterable<ObjectInfo> objects = bucket.listObjects(
ObjectListOption.prefix("some/path/prefix"), ObjectListOption.recursive(true));
for (ObjectInfo object : objects) {
// Do something for each object.
}
}
Scope scope = ...;
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope);
Bucket bucket = project.openBucket("my-bucket", scope)) {
bucket.deleteObject("path/to/my/object"));
}
Sharing content on the Storj network is achieved by sharing the following pieces of information to the receiving party:
Scope
to access the shared content.Below is an example for uploading a file and preparing the restricted Scope
:
Scope scope = ...;
String tempDir = mActivity.getCacheDir().getPath();
try (Uplink uplink = new Uplink(UplinkOption.tempDir(tempDir));
Project project = uplink.openProject(scope);
Bucket bucket = project.openBucket("my-bucket", scope);
InputStream in = new FileInputStream("path/to/local/file")) {
bucket.uploadObject("path/to/my/object", in);
}
// Share a read-only scope to "my-bucket/path/to/my" folder.
// It is possible to restrict the scope only to "my-bucket/path/to/my/file" object too.
Scope sharedScope = scope.restrict(
new Caveat.Builder().disallowDeletes(true).disallowWrites(true).build(),
new EncryptionRestriction("my-bucket", "path/to/my"));
// Serialize the scope to, so it can be easily sent to the receiving party.
String serializedShare = sharedScope.serialize();
The receiving party can download the shared file using the following example:
// Info received by the other party
String serializedScope = "13GRuHAW...";
String bucketName = "my-bucket";
String objectPath = "path/to/my/object";
Scope scope = Scope.parse(serializedScope);
try (Uplink uplink = new Uplink();
Project project = uplink.openProject(scope);
Bucket bucket = project.openBucket(bucketName, scope);
OutputStream out = new FileOutputStream("path/to/local/file")) {
bucket.downloadObject(objectPath, out);
}