10 Storage
Oracle Mobile Cloud provides a Storage API for storing media in the cloud. As a mobile app developer, you can use this API in your mobile app to store and retrieve objects, such as files, text, images, and JSON objects.
What Can I Do with Storage?
The Storage API enables your mobile app to store, update, retrieve, and delete media, such as JSON objects, text files, and images, in collections in your Mobile Hub instance. The media are stored as opaque objects, which means that each object is stored and retrieved from the collection by a user- or system-generated GUID (globally unique ID). You use mobile user roles to control who can read and write the objects in the collection.
Note that this API isn’t intended to act as a database-as-a-service (DBaaS) solution by storing business data used by external systems, or to host HTML 5 applications like a content management system (CMS).
Android
Add an Object to a Collection
String text = "This is sample text file";
String name = "sampleText.txt";
StorageObject storageObject = new StorageObject(null, text.getBytes(), "text/plain");
storageObject.setDisplayName(name);
Fetch an Object
This fetches the storage object from a collection and reads its contents in a stream:
int i=0;
for (StorageObject storageObject: storageObjects) {
i++;
InputStream payload = storageObject.getPayloadStream();
int n;
char[] buffer = new char[1024 * 4];
InputStreamReader reader = null;
try {
reader = new InputStreamReader(payload, "UTF8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
StringWriter writer = new StringWriter();
assert reader != null;
try {
while (-1 != (n = reader.read(buffer))) {
writer.write(buffer, 0, n);
}
}catch (IOException e){
e.printStackTrace();
}
Logger.debug(TAG, "Storage Object "+i+" "+writer.toString());
}
Get Multiple Objects from a Collection
List<storageObject> storageObjects = null;
try {
Storage storage = mobileBackend.getServiceProxy(Storage.class);
storageObjects = storageCollection.get(0,10,true);
} catch (ServiceProxyException e) {
e.printStackTrace();
}
Get a Shared Collection
This gets a specific shared collection called sharedCollection:
StorageCollection storageCollection= null;
try {
Storage storage = mobileBackend.getServiceProxy(Storage.class);
storageCollection = storage.getStorageCollection("sharedCollection");
} catch (ServiceProxyException e) {
e.printStackTrace();
}
Retrieve an Object
private Storage mStorage;
private String collectionID = "YOUR_COLLECTION_ID";
private String objectID = "YOUR_OBJECT_ID";
...
try {
//Initialize and obtain the storage client
mStorage = MobileManager.getManager().getDefaultMobileBackend(this).getServiceProxy(Storage.class);
//Fetch the collection
StorageCollection collection = mStorage.getStorageCollection(collectionID);
//Fetch the object
StorageObject object = collection.get(objectID);
//Get the payload
InputStream payload = object.getPayloadStream();
//Display the image
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.setImageBitmap(BitmapFactory.decodeStream(payload));
} catch (ServiceProxyException e) {
e.printStackTrace();
}
Update an Object
StorageObject storageObject = null;
try {
Storage storage = mobileBackend.getServiceProxy(Storage.class);
storageObject = storageCollection.get("26651715-9259-4676-a035-df47ef3e7e79");
} catch (ServiceProxyException e) {
e.printStackTrace();
}
String text = "This is modified text in a text file";
storageObject.setPayload(text.getBytes(), "text/plain");
try {
Storage storage = mobileBackend.getServiceProxy(Storage.class);
storageCollection.put(storageObject);
} catch (ServiceProxyException e) {
e.printStackTrace();
}
iOS
Add an Object to a Collection
- (void) uploadData{
NSString* collection_Id = @"myCollection";
NSString* payload = @"This is a simple text object";
NSString* contentType = @"text/plain";
if ( payload == nil || [payload isEqualToString:@""])
{
NSLog(@"There is nothing to upload");
}
else{
// Get storage object.
OMCStorage* storage = [mbe storage];
// Get collection where you want to upload new data.
OMCStorageCollection* aCollection = [storage getCollection:collection_Id];
// Create new data from payload (in case your payload is not already in NSData format )
NSData* payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
OMCStorageObject* aObject = [[OMCStorageObject alloc] setPayloadFromData:payloadData
withContentType:contentType];
// Post data.
[aCollection post:aObject];
NSLog(@"Upload finished");
}
}
Delete an Object
NSString* collection_Id = @"";
// Get your collection
OMCStorageCollection* aCollection = [storage getCollection:collection_Id];
// Create/Update an object with the same objectID.
NSString* objectID = @"object2";
BOOL isDeleteSuccessful = [aCollection deleteWithKey:objectID];
Download Data to a Collection
This downloads data from any storage collection where:
collectionID
is the id for the target collection.
objectID
is the id for the target object.
-(void) downloadData{
NSString* collection_Id = @"";
NSString* object_Id = @"";
// Get storage object.
OMCStorage* storage = [mbe storage];
// Get your collection
OMCStorageCollection* aCollection = [storage getCollection:collection_Id];
// Get your object from your collection.
OMCStorageObject* anObject = [aCollection get:object_Id];
// Get the data from payload of your object.
NSData* data = [anObject getPayloadData];
NSLog(@"Download finished");
}
Get a User Isolated Collection
NSString* collection_Id = @"";
NSString* user_Id = @"";
// Get user isolated collection.
OMCStorageCollection* aCollection = [storage getCollection:collection_Id forUserId:user_Id];
Get Multiple Objects from a Collection
NSString* collection_Id = @"";
// Get your collection.
OMCStorageCollection* aCollection = [storage getCollection:collection_Id];
NSUInteger offset = 0; NSUInteger limit = 10;
NSArray<OMCStorageObject*>* objects = [collection get:offset withLimit:limit getAllObjects:NO];
Get Object Data as a Stream
NSString* collection_Id = @"";
OMCStorageCollection* aCollection = [storage getCollection:collection_Id];
NSString* object_Id = @"";
OMCStorageObject* anObject = [aCollection get:object_Id];
NSInputStream* inStream = [anObject getPayloadStream];
Retrieve a Storage Object
- (void) downloadData{
//Fill in IDs for collection and object.
NSString* collection_Id = @"";
NSString* object_Id = @"";
// Get storage object.
OMCStorage* storage = [mbe storage];
// Get your collection.
OMCStorageCollection* aCollection = [storage getCollection:collection_Id];
// Get your object from your collection.
OMCStorageObject* aObject = [aCollection get:object_Id];
// Get the data from your object's payload.
NSData* data = [aObject getPayloadData];
NSLog(@"Download finished");
}
Updating an Object
NSString* collection_Id = @"";
// Get your collection.
OMCStorageCollection* aCollection = [storage getCollection:collection_Id];
// Create/Update object with the same objectID.
NSString* objectID = @"";
NSData* payload = [@"This is updated object" dataUsingEncoding: NSUTF8StringEncoding];
OMCStorageObject* object = [[OMCStorageObject alloc] initPayload:objectID
withData:payload
andContentType:@"plain/text"];
OMCStorageObject* returnedObject = [aCollection put:object];
Uploading Data to a Collection
-(void) uploadData{
NSString* collection_Id = @"";
NSString* payload = @"";
NSString* contentType = @"";
if ( payload == nil || [payload isEqualToString:@""])
{
NSLog(@"There is nothing to upload");
}
else{
// Get the storage object from your MobileBackend object.
OMCStorage* storage = [mbe storage];
// Get the collection where you want to upload new data.
OMCStorageCollection* aCollection = [storage getCollection:collection_Id];
// Create new data from payload (in case your payload is not already in NSData format).
NSData* payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
OMCStorageObject* anObject = [[OMCStorageObject alloc] setPayloadFromData:payloadData
withContentType:contentType];
// Post data to the collection.
[aCollection post:anObject];
NSLog(@"Upload finished");
}
}
Cordova, JavaScript, and TypeScript
Add an Object to a Collection
var obj = new mcs.StorageObject(collection);
obj.setDisplayName("XYZ.pdf");
obj.loadPayload("Hello World from Oracle Autonomous Mobile Cloud Enterprise Cordova SDK", "text/plain");
collection.postObject(obj).then(onSuccess, onFailure);
function onSuccess(collection) {
console.log(collection);
return collection;
}
function onFailure(error) {
console.error(error);
return Promise.reject(error);
}
Delete an Object
collection.deleteObject(objectId)
.then(onDeleteObjectSuccess)
.catch(onDeleteObjectFailure);
function onDeleteObjectSuccess(response) {
console.log(response);
return response;
}
function onDeleteObjectFailure(error) {
console.error(error);
return Promise.reject(error);
}
Fetch an Object
This fetches the storage object from a collection and reads its contents in a stream:
collection.getObject(objectId, 'json')
.then(onGetObjectSuccess)
.catch(onGetObjectFailed);
function onGetObjectSuccess(object)
{ console.log(object);
return object;
}
function onGetObjectFailed(error)
{ console.error(error);
return Promise.reject(error);
}
Get a Collection
var backend = mcs.mobileBackend;
backend.storage.getCollection(collectionName)
.then(onGetCollectionSuccess)
.catch(onGetCollectionFailed);
function onGetCollectionSuccess(collection){
console.log(collection);
return collection;
}
function onGetCollectionFailed(error) {
console.error(error);
return Promise.reject(error);
}
Get an Object from a User Isolated Collection
This gets an object from a user isolated collection belonging to another user:
let backend = mcs.mobileBackend;
backend.storage.getCollection(collectionName, userId)
.then(onGetCollectionSuccess)
.catch(onGetCollectionFailed);
function onGetCollectionSuccess(collection) {
console.log(collection);
return collection;
}
function onGetCollectionFailed(error){
console.error(error);
return Promise.reject(error);
}
Get Multiple Objects from a Collection
Gets a collection, then uses that collection to get multiple objects:
collection.getObjects(2, 3, false)
.then(onSuccess)
.catch(onFailure);
function onSuccess(collection) {
console.log(collection);
return collection;
}
function onFailure(error) {
console.error(error);
return Promise.reject(error);
}
Update an Object
collection.getObject(objectId)
.then(onGetObjectSuccess)
.then(onSaveObjectSuccess)
.catch(onGetObjectFail);
function onGetObjectSuccess(response){
response.name = 'NewName';
return collection.putObject(response);
}
function onSaveObjectSuccess(response){
console.log(response);
return response;
}
function onGetObjectFail(error){
console.error(error);
return Promise.reject(error);
}
REST API
Storage API Endpoints
The Storage API has endpoints for retrieving, paginating, and ordering collections and also for retrieving, updating, and removing objects.
Here, we give a brief overview of the Storage API endpoints.
Get a Single Collection
To get the metadata about a collection, such as ID, description, and whether it is user isolated, call the GET
operation on the {collection}
endpoint as follows:
GET {baseUri}/mobile/platform/storage/collections/{collection}
images
:GET {baseUri}/mobile/platform/storage/collections/images
Get All Collections Associated with a Mobile Backend
To get a list of the collections that are associated with a mobile backend, call the GET
operation on the collections
endpoint as follows:
GET {baseUri}/mobile/platform/storage/collections
Store an Object
The Storage API has two operations for creating objects. The operation that you use depends on if you want to specify the object’s ID or you want the ID to be generated automatically.
-
To specify the ID, use
PUT
, and put the ID in the URI as described in Specifying the Object Identifier. Note that you can use theIf-None-Match
header to ensure that you don’t overwrite an object that has the same ID, as described in Creating an Object (If One Doesn't Already Exist). -
To generate an ID, use
POST
as described in Generating an Object Identifier .
When you create an object using your own ID, remember that, for shared collections, the ID must be unique to the collection. For user isolated collections, the ID must be unique to the user’s space.
Always include the Content-Type
header to specify the media type of the object being stored. This property also specifies the media type to return when the object is requested. If you don’t include this header, then the content type defaults to application/octet-stream
.
Note that Storage doesn’t transform or encode an object. Storage stores the exact bytes that you send in the request. For example, you can’t send a Base-64 encoded image and store it as a binary image by including a Content-Type
header set to image/jpeg
and a Content-Encoding
header set to base64
. You can use a custom API to perform the transformation for you, as shown in the code examples in storage.store(collectionId, object, options, httpOptions) .
Specify the Object Identifier
When performing a PUT
operation, the identifier of the object corresponds to the last value specified in the URI. For example, to store an object with an ID called part1524
:
PUT {baseUri}/mobile/platform/storage/collections/images/objects/part1524
Create an Object (If One Doesn't Already Exist)
*
) character in the request's If-None-Match
header to force the PUT
operation to create the object with the specified object ID only if no other object exists with that ID. Specifying the wildcard causes the call to fail if another object already exists with the same ID. For example:PUT {baseUri}/mobile/platform/storage/collections/images/objects/part1542
Headers:
If-None-Match: *
Generate an Object Identifier
POST
operation. Unlike the PUT
operation, there’s no identifier specified at the end of the URI for a POST
operation. For example:POST {baseUri}/mobile/platform/storage/collections/images/objects
The URI that accesses the newly created object is returned through the Location
header in the response, and the ID
attribute is included in the response body.
What Happens When an Object is Created?
When an object is created:
-
The content is stored.
-
The value of the
Content-Type
field in the request is stored. (This becomes theContent-Type
field definition returned when the object is requested using aGET
operation.) -
An entity tag (ETag) value is assigned.
-
The
createdBy
value is set to the user ID of the user who performed the create operation. -
The
createdOn
value is set to the time the object was stored on the server.
Update an Object
Objects are updated using the PUT
operation. For the PUT
call, specify the same identifier that was specified or generated when the object was created. Because objects are opaque, updating an object completely replaces the previous contents.
What Happens When an Object Is Updated?
When a PUT
is performed on an object, the following occurs:
-
The content is completely replaced.
-
The value of the ETag changes.
-
The
modifiedBy
value is set to the user ID for whom the mobile app performed thePUT
operation. -
The
modifiedOn
value is set to the time the object was stored on the server.
Optimistic Locking
Optimistic locking is a strategy to use when you want to update an object only if object was not updated by someone else after you originally retrieved it. To implement this strategy, do one of the following:
-
Put the timestamp of when you last retrieved the object in the
If-Unmodified-Since
header. -
Put the object’s ETag in the
If-None-Match
header.
2
, then the PUT
operation in the following example is performed only when the If-None-Match
value of "2"
matches the ETag of the object (part1524
). If the versions don’t match, then the call’s PUT
operation isn’t performed and part1524
remains unchanged.PUT{baseUri}/mobile/platform/storage/collections/images/objects/part1524
Headers:
If-None-Match: \"2\"
If-Unmodified-Since
:PUT {baseUri}/mobile/platform/storage/collections/images/objects/part1524
Headers:
If-Unmodified-Since: Mon,30 Jun 2014 19:43:31 GMT
Retrieve a List of Objects
To get the metadata about a set of objects in a collection, use the GET
operation on the /collections/{collection}/objects
endpoint. This metadata includes the object’s ID, its name, and size. The metadata also includes the canonical link and self links. For a full list of properties, see Taking a Look at Object Metadata .
images
is the name of a shared collection.GET {baseURI}/mobile/platform/storage/collections/images/objects
If the collection is user isolated and you have READ_ALL
or READ_WRITE_ALL
access, then you must include the user
query parameter and specify which user's objects you want listed, even if you want to see your own objects (use *
to list all user’s objects). Note that you provide the user’s ID, not the user name. For example:
GET {baseURI}/mobile/platform/storage/collections/images/objects?user=0cea04ee-9e26-4de3-ad6b-00a66c8d3b96
Page Through a List of Objects
If you don’t want to see all the results, or if you want to get the results in small blocks, use the limit
and offset
query parameters to request a subset of items.
Use the limit
parameter to restrict the number of items returned. The default is 100
. Define offset
as the zero-based starting point for the returned items. The returned JSON body contains links for retrieving both the next and previous sets of items.
Get {baseUri}/mobile/platform/storage/collections/images/objects?offset=200&limit=50
Order
orderBy
parameter to control the order of the returned items. You can specify which property to order on and specify whether to put the items in ascending (asc
) or descending (desc
) order:Get {baseUri}/mobile/platform/storage/collections/images/objects?orderBy=contentLength:desc
You can sort by the name
, modifiedBy
, modifiedOn
, createdBy
, createdOn
, or contentLength
property.
Note that you can order by one property only (either asc
or desc
).
Query
q
query parameter to restrict the list of returned objects to the value specified for the id
, name
, createdBy
, or modifiedBy
attributes.Get {baseUri}/mobile/platform/storage/collections/images/objects?q=part
The objects returned are based on a case-sensitive, partial match of the id
, name
, createdBy
, and modifiedBy
attributes. With this example, the results might include an item with an ID of part1524
and an item modified by bonapart
.
Retrieve an Object
Use the GET
operation to retrieve the entire object. When performing the GET
operation, the identifier (such as part1524
in the following example) is specified at the end of the URI.
Storage always returns the exact bytes that were stored. If the Accepts
header doesn’t match the Content-Type
that the object was stored with, then it returns a 406
status code.
Get {baseUri}/mobile/platform/storage/collections/images/objects/part1524
Headers:
If-None-Match: \"2\"
Delete an Object
DELETE
operation. Deleting an object is permanent. There’s no way to restore an object after you call this operation.DELETE {baseUri}/mobile/platform/storage/collections/images/objects/part1524
If-None-Match
header with the object’s ETag, or the If-Unmodified-Since
header with the timestamp of when you last retrieved the object:DELETE {baseUri}/mobile/platform/storage/collections/images/objects/part1524
Headers:
If-None-Match: \"2\"
You can use these headers to prevent overriding a change that another user made after you originally retrieved the object.
Optimize Performance
You can use the Check If Exists, Get If Newer, and Read Part of an Object (Chunk Data) strategies discussed next to optimize performance when you retrieve an object:
Check If Exists
To check if an object exists, use the HEAD
operation instead of a GET
operation. The HEAD
operation returns the same information except for the actual object value.
Put If Absent
You can use the If-None-Match
header with a wildcard (*
) value in a PUT
operation to store an object only when (or if) it isn’t already included in the collection.
PUT {baseUri}/mobile/platform/storage/collections/profiles/objects/uprofile
Headers:
If-None-Match: *
In this example, if the uprofile
object doesn’t have an ETag, then myProfile.txt
is stored as the uprofile
object.
Get If Newer
If you have already retrieved an object, and you want to re-fetch it only if it has changed, use the GET
operation with the If-None-Match
or If-Modified-Since
header to retrieve the object only if there has been a change since the last time the object was fetched.
-
If-None-Match
This example re-fetches the object only if the ETag is not
2
.GET {baseUri}/mobile/platform/storage/collections/images/objects/part1542 Headers: If-None-Match: \"2\"
-
If-Modified-Since
This example re-fetches the object only if it was modified after the date and time specified. Otherwise, the response status is
304 not modified
.GET {baseUri}/mobile/platform/storage/collections/images/objects/part1542 Headers: If-Modified-Since: Mon, 30 Jun 2014 19:43:31 GMT
Read Part of an Object (Chunk Data)
If the mobile app needs to get a large object like a video file, you can use the Range
header to retrieve a subset of the object. This field lets the mobile app retrieve the data in chunks, rather than all at once, by requesting a subset of bytes. Using this strategy, you can start streaming a video, or start displaying the contents of a long list before you fetch the whole object.
Here are examples of byte-range specifier values:
-
First 100 bytes:
bytes=0-99
-
Second 100 bytes:
bytes=100-199
-
Last 100 bytes:
bytes=-100
-
First 100 and last 100 bytes:
bytes=0-99,-100
This example gets the first 100 and last 100 bytes of a profile to display a preview of the object’s contents:
GET {baseUri}mobile/platform/storage/collections/profiles/objects/uprofile
Headers:
Range: bytes=0-99,-100
Test Runtime Operations Using the Endpoints Page
You can test client REST calls for collections manually through a command line tool or utility, from a mobile app running on a device or simulator, or you can use the Endpoints page to test various operations.
Using the Endpoints page for the Storage API, you can try out basic collection calls, which would typically be exercised by a mobile app. These endpoints would be called directly by calling REST APIs, indirectly (by calling the client SDK), or through custom code. Instead of configuring a device or simulator, or entering the command manually, you can test the API by first entering mobile app user credentials and parameters appropriate to the call and then by clicking Test Endpoint. The page displays the payload and the status code.
You can access the Endpoints page by clicking Storage in Platform APIs section that is located at the bottom of the APIs page for a mobile backend. You can also open the page by clicking Storage in the Platform APIs section at the bottom of the APIs page. (You open this page by clicking to open the side menu. You then click Development and then APIs).
Manage Collections
Mobile apps can only use collections that are associated with a backend. You can make this association by adding existing collections to the backend when you create it. You can also create new collections as part of this process.
You can also use the Storage configuration pages in the service UI to associate a collection with a backend, as well as create and configure a collection, and define whether the collection is shared or user isolated.
Shared and User Isolated Collections
A collection is either shared or user isolated.
When a collection is shared, no one owns the collection or an object, and the objects are kept in a shared space. Those with certain mobile user roles, permissions, and access to the backend, or anonymous access to the backend associated with the collection, can update an object. Note that in both shared and user isolated collections, each object has an ID that is unique to the collection.
When a collection is user isolated, users who have Read-Only (All Users)
access can read objects in other users’ spaces. Users with Read-Write (All Users)
access can both read and write objects in other users’ spaces. Anonymous access is not permitted in user isolated collections.
Let's look at some examples of this behavior using the following scenarios:
Shared Collection
-
Ben has contributed articles on bugs and bats, while Art has written about cows and dogs.
-
The dogs article is shared, allowing both Ben and Art to collaborate on it.
-
Art and Ben are able to modify any article regardless of who originally submitted it.
-
Dee can read all the articles, but she can't make changes.
Security_CollectionsAnonymousAccess
environment policy, then Ben, Art, Dee or anyone who has access to the backend can submit, change, or read articles.
User Isolated Collection
An online magazine has provisioned a user isolated collection called Articles, as shown in the following figure.
-
Ben and Art can read and edit their articles, and upload new articles as well. They can’t read or write each other's files.
-
Dee can read only her article. Because her role is
InactiveAuthor
, which gives herRead-Only
permission, she can't upload any new articles. -
Eva, the editor, can make changes to any file and return it to the author's isolated space.
-
Raj, the publisher, can view all the articles, but he can't make changes.
-
Because users are isolated, the authors don't have to worry about naming conflicts with others. Objects in different isolation spaces can have the same name (as is the case for the “dogs” articles by Dee and Art).
-
Eva and Raj can access Ben, Art, and Dee’s objects only by specifying a user qualification parameter. When Eva wants to make changes to Art’s article, the call that enables her to write to Art’s user space must include Art’s ID.
Anonymous users don’t have access to user isolated collections. If a user isolated collection is added to the Security_CollectionsAnonymousAccess
environment policy, it’s just ignored.
Permissions in Shared and User Isolated Collections
You can designate who can access and update objects in a collection by attaching access permissions to mobile user roles, or for anonymous access, by adding the shared collection name to the Security_CollectionsAnonymousAccess
environment policy.
For example, to include the Articles
collection use Security_CollectionsAnonymousAccess=Articles
.
If the collection does not, or cannot permit anonymous access:
-
Art and Ben’s
Author
mobile user role is associated with theRead-Write
permission.-
In the shared collection, they can read and update any article within the shared collection.
-
In the user isolated collection, they can read and update their own articles.
-
-
In contrast, Dee has the
InactiveAuthor
mobile user role, which gives herRead-Only
permission.-
In the shared collection, Dee can read Art’s article about dogs, as well as various articles from either Art or Ben about bugs, cows, and bats. Unlike Ben or Art, she can’t delete articles or add new ones.
-
In the user isolated collection, she can read her own article about dogs, but she can’t read Art’s article about dogs.
-
-
For user isolated collections, mobile user roles that are associated with the
Read-Only (All Users)
permission can view any object. TheRead-Write (All Users)
permission allows users to view and update objects in other users’ spaces. Because her role asEditor
has aRead-Write (All Users)
permission, Eva can read and edit various authors’ files, such as those authored by Ben and Art.
Editor
), Ben (Author
), and Art (also Author
), in the user isolated collection, the objects remain in their respective isolated spaces.
Note that when anonymous access is allowed on a shared collection, access and the ability to update an object is granted to any authenticated user as well, regardless of role. This means adding a collection name to the Security_CollectionsAnonymousAccess
environment policy overrides permissions given through roles. Take care when allowing anonymous access to a collection. Security is more limited than with role-based permissions.
Storage Configuration
The Storage configuration pages in the UI can help you with a variety of tasks, such as creating and editing collections, and associating backends with collections.
Storage Configuration for All Collections
There are two Storage configuration pages you can use: Manage all collections in your instance from the > Mobile Apps >Development>Storage page. Manage collections for a specific backend from the Storage tab on the backend page.
To open the Storage page for all collections, click > Development>Storage.
Using this page, you can create collections, edit existing ones, associate them with mobile backends, and publish them.
You can find out when the collections listed were created or updated and which backends are using them by first selecting a collection and then expanding Used By and History.
Storage Configuration for a Specific Backend
To manage collections for a specific backend, click > Development>Backends > Storage. This page shows which collections are associated with the backend and allows you to create and update associated collections.
Define a Collection
The New Collection dialog lets you name a collection so that it can be identified in REST calls and designate it as shared or user isolated.
-
Open the Storage page either from a mobile backend or by clicking Storage in the side menu, and click New Collection.
-
Complete the New Collection dialog:
-
Enter a name for your collection. This name is used to form the Universal Resource Identifier (URI) for the collection. Within the context of the API call, the collection name is referred to as the collection ID:
{baseUri}/mobile/platform/storage/collections/{collection ID}
For example, for a collection named FiF_UploadedImages (cloud storage of images uploaded from mobile apps), the URI call would look like this:{baseUri}/mobile/platform/storage/collections/FiF_UploadedImages
For a closer look at Storage API syntax, see Storage API Endpoints.
-
Choose the collection type:
Shared
orUser Isolated
. You can’t change the scope of the collection after you’ve set it. For details and examples, see Shared and User Isolated Collections. -
If needed, enter a short description for the purpose of the collection, to be displayed in the list of collections.
-
-
Click Create.
-
You can modify the collection name, access permissions, and its contents. Remember, you can’t change the collection type after it’s created.
-
You can version a collection. You might want to increment a collection’s major and minor version numbers when you publish it or when you add new objects.
-
While in the draft state, a collection can be moved to the trash from the More menu.
Collection Metadata
In addition to the basic properties like size (in bytes), and description, the collection metadata includes the collection name that identifies it for REST calls.
Property | Value Type | Description |
---|---|---|
|
string |
The short description. This is an optional value. |
|
string |
The collection name, which is used in the uniform resource identifier (URI). For example:
The collection name is case-sensitive, meaning that |
Add Access Permissions to a Collection
Collection access is granted through anonymous user settings in the policy file, or managed by mobile user roles. Once a mobile user role is defined, you can also grant which roles can read and write objects in the collection. To see what mobile user roles are available, go to the My Profile UI and click Roles.
Anonymous Access to Collections
Anonymous access is often given to users who just want to check information on an app without having to log in or needing a defined role. Weather apps, where a user can check their local weather, are a good example of this.
Likewise, you can grant anonymous access to a shared collection. Once a shared collection is created, the administrator adds its name to the Security_CollectionsAnonymousAccess
policy. You can then access the shared collection via the REST API or the client SDK for your mobile platform. Also, if you want to access this anonymous shared collection from the UI, a workaround is to grant Read-Write
permission to any role on the properties page.
Keep in mind that when you add a shared collection to the policy, both anonymous and named users have access and read/write privileges to the collection.
Role-based Access to Collections
Add Objects to a Collection
You can populate a collection with objects.
- On the Storage page, select a collection and click Open.
- If this collection has no objects, click Upload Files and then browse to and retrieve the object. Click Open.
- If this collection already has objects, click Upload in the Content page. Browse to and retrieve the object. Click Open.
- If the collection is shared, click Add. If you have the identity domain administrator role, you can also upload to user isolated collections. Add the user realm and user name to the User Name Required dialog, and click Ok. You can only select from users whose roles have been granted permission to the collection. (Assign these roles in the Properties page.)
- To view the object data, select it from the list.
To permanently remove an object from a collection, select it and click Delete.
Object Metadata
When you upload an object, the Content page displays basic metadata, such as size, content type, version information, and who uploaded it. Using this page, you can also delete unneeded objects, or filter them. Some functions in user isolated collections are only available if you have the identity domain administrator role.
Property | Value Type | Description/Usage |
---|---|---|
|
string |
The object name, which is used for operations on a single object. It is the last value specified in the URI. |
|
integer |
The size, in bytes. |
|
media type |
The media type for the data, such as |
|
string (an integer in quotes, for example, |
A value that represents the version of the object. It's used with the |
|
user name |
The name of the user who uploaded the data. |
|
time stamp (In ISO 8601) |
The time that the object was most recently stored on the server. Time stamps are stored in UTC. |
|
user name |
The name of the user who modified the object. |
|
time stamp (in ISO 8601) |
The time when the server received a request for an object. Time stamps are stored in UTC. |
|
string |
For a user isolated collection, the ID of the user whose space the object is in. |
Update the Collection
You can update the name, description and access to a collection. You can’t however, change the collection type.
- On the Storage page, select a collection and then click Open.
- Click Properties. (The Properties page opens by default when you first create a collection. On subsequent visits, the Content page opens by default.)
- Change the name, description or access as needed.
- Click Save.
Offline Data Storage
The client SDK’s Sync Client library, in conjunction with the Storage library, enables mobile apps to cache a collection’s objects for offline use and performance improvement. The apps can then use the cached objects instead of re-retrieving them from Storage, as described in How Synchronization Works with the Storage APIs. If a collection’s content changes infrequently, then consider enabling those mobile apps to cache the collection’s objects by selecting Enable the mobile client SDK to cache collection data locally for offline use.
When Enable the mobile client SDK to cache collection data locally for offline use is selected, the objects that a mobile app retrieves can remain in the cache for the period set in the Sync_CollectionTimeToLive
policy. This value is conveyed to the app through the Oracle-Mobile-Sync-Expires
response header. By default, the timeout period is set for 24 hours (86,400 seconds).
Don’t select this option for time-critical data, where a cached value might be misleading. For example, if the collection contains current stock prices, you shouldn’t select this option, because users expect the latest value (or no value at all).
If your mobile app isn’t using the client SDK’s Storage library, and your app is caching Storage objects, then you can take advantage of the following request and response headers:
Type | Header | Description |
---|---|---|
Request | Oracle-Mobile-Sync-Agent |
When this header is set to true in the request, then the response includes either Oracle-Mobile-Sync-Expires or Oracle-Mobile-Sync-No-Store .
|
Response | Oracle-Mobile-Sync-Expires |
Specifies when the returned resource must be marked as expired. Uses RFC 1123 format, for example EEE, dd MMM yyyyy HH:mm:ss z for SimpleDateFormat . This value is determined by the Sync_CollectionTimeToLive policy.
|
Response | Oracle-Mobile-Sync-No-Store |
When set to true , the client mustn’t cache the returned resource.
|
To learn more about data caching, see Data Offline and Sync .
Associate a Collection with a Backend
Associating a collection makes its contents available to a specific backend. The associated collection is a dependency.
-
In the Storage page, select a collection.
-
Click More and then select Associate Backends.
-
In the Associate Backends dialog, select one or more backends from the list.
-
Click Add.
In the details pane, you can see any associated backends by expanding Used By.
You can also associate a collection with a backend this way:
-
Open the backend.
-
Click the Storage tab and then choose Select Collections.
-
Choose one or more collections from the Select Collections dialog, and then click Select.
Remove a Collection from a Backend
You might want to disassociate a collection from a backend so that you can change the backend's state without affecting the collection. Or you might want to disassociate the collection and associate a different one.
-
In the Storage page, select a collection.
-
In the Details section on the right, view the Used By list.
-
To delete the association, click the X that follows the backend version number.
-
You’ll be prompted to remove the dependency. Click Remove.
To remove a collection from a backend:
-
Open the backend.
-
Open the Storage page.
-
Click the X adjacent to the collection that you want to remove.
-
In the Confirm Remove Dependency dialog, click Remove.