Files
Baqend comes with a powerful File and Asset API. You can create multiple root level folders and apply different permissions to those. Files can be uploaded, replaced, downloaded and deleted when the user has the right permissions.
In addition the SDK comes with a rich set of functionality to transform the file contents to different browser friendly formats. In the following table we list all supported file formats:
type | JavaScript type | Description |
---|---|---|
'arraybuffer' | ArrayBuffer | The content is represented as a fixed-length raw binary data buffervar buffer = new ArrayBuffer(8) |
'blob' | Blob|File | The content is represented as a simple blobvar blob = new Blob(["<a href=..."], {type : 'text/html'}) Note: This does not work in Baqend code |
'json' | object|array|string | The file content is represented as jsonvar json = {prop: "value"} |
'text' | string | The file content is represented through the string'A Simple Text' |
'base64' | string | The file content as base64 encoded string'PHN2ZyB4bWxucz...' |
'data-url' | string | A data url which represents the file content'data:image/gif;base64,R0lGODlhD...' |
'stream' | Stream | A stream containing the file content See our example. Note: This only works in Baqend code. |
'buffer' | Buffer | A buffer containing the file content'var buffer = Buffer.from(array)' Note: This only works in Baqend code |
The file API accepts all the listed formats as upload type and transforms the content to the correct binary representation
while uploading it. The SDK guesses the correct type except for the base64
type and transforms it automatically.
When you download a file you can specify in which format the downloaded content should be provided.
Accessing Files
The simplest way to access a file is to retrieve the absolute url form the Baqend SDK. Therefore you can use any existing file reference or you can create one by yourself.
The are multiple ways to reference a file:
let file;
// Absolute references have to start with '/file' followed by a root folder e.g. '/www'
file = new DB.File('/file/www/myPic.jpg');
// Alternatively you can give the path of the file, starting with the root folder
file = new DB.File({ path: '/www/myPic.jpg' });
// Or you specify the name and parent (folder) of the file
file = new DB.File({ parent: '/www', name: 'myPic.jpg' });
// Because '/www' is the default parent in can be omitted
file = new DB.File({ name: 'myPic.jpg' });
To get the full url to access the file just use the file.url
shorthand. This ensures that the domain is correctly used,
checks if the file is stale or can be directly served form the cache and attach authorization credentials if needed.
In a common html template engine you can just write:
<img src="{{file.url}}">
You can also manage your files in folders for example like this:
let file;
// Creates the same file reference
file = new DB.File('/file/www/images/myPic.jpg');
// Parent start with the root folder, e.g. /www and followed by additional folders
file = new DB.File({parent: '/www/images', name: 'myPic.jpg'});
Embedded Files
Files can also be embedded in other objects like for example a profile image in a user object (see primitive types):
DB.User.me.load().then((user) => {
const file = user.profileImage;
// The file url, e.g. 'http://app.baqend.com/v1/file/users/img/myImg.png'
console.log(file.url);
});
Metadata
Suppose you have an uploaded file /www/images/myPic.jpg
and a reference to it.
Then you can't use the file metadata (mimeType, acls, size, etc.) before you actually download the file
or load its metadata.
Therefore you can use the loadMetadata
method to get additional file metadata
(not the content itself):
const file = new DB.File('/file/www/images/myPic.jpg');
file.loadMetadata(() => {
console.log(file.isMetadataLoaded); // true
console.log(file.lastModified); // The time of the last update
console.log(file.size); // file size in bytes
});
To actually check if the metadata of a file is actually available, you can use the isMetadataLoaded
property.
file.isMetadataLoaded // > false if the metadata was not previously fetched by file.loadMetadata() or file.doanload()
Listing Files
You can also list all files inside a folder. Either provide the path to the folder as string or a file reference representing the folder
var folder = new DB.File('/file/www/images/');
DB.File.listFiles(folder).then(function(files) {
// all the files in the folder '/www/images/'
});
You can also list all root folders:
DB.File.listBuckets().then(function(rootFolders) {
// all root folders
});
Uploading Files
To upload a file you must first create a file with its name and its content.
Afterwards you can simply upload the file by just invoking upload()
:
const file = new DB.File({ name: 'test.png', data: file, type: 'blob' })
file.upload().then((file) => {
// Upload succeed successfully
console.log(file.mimeType); // contains the media type of the file
console.log(file.lastModified); // the upload date
console.log(file.eTag); // the eTag of the file
}, (error) => {
// Upload failed with an error
});
In most cases you would like to upload the files which was provided by your user through a file input field or a file drag & drop event.
<input type="file" id="input" multiple onchange="uploadFiles(this.files)">
function uploadFiles(files) {
const pendingUploads = [];
for (let i = 0, numFiles = files.length; i < numFiles; i++) {
// If you omit the name parameter, the name of the provided file object is used
const file = new DB.File({data: files[i]});
pendingUploads.push(file.upload());
}
Promise.all(pendingUploads).then(() => {
// All files are successfully uploaded
});
}
In the cases you want to upload base64 encoded binary data you can use the base64 type in the options object:
const file = new DB.File({ name: 'test.png', data: 'R0lGODlhDAAeALMAAG...', type: 'base64', mimeType: 'image/gif' });
file.upload().then((file) => {
// Upload succeed successfully
console.log(file.mimeType); // contains the media type of the file
console.log(file.lastModified); // the upload date
console.log(file.eTag); // the eTag of the file
}, (error) => {
// Upload failed with an error
});
If you try to overwrite an existing file and do not have previously fetched the file or its metadata, or the file has
been changed in the meantime the upload will be rejected to prevent accidental file replacement.
If you like to skip the verification, you can pass the {force: true}
option to the upload()
call.
Example: Upload from external URL
To illustrate how to do uploads with Baqend files, consider a simple use case: You have a third-party URL that you would like to download into a Baqend file. To do this, create a new module "download" in the dashboard and paste the following code:
const http = require('http');
/**
* Fetches a URL and saves it to a Baqend file.
*
* Usage example:
* download = require('./download');
* download.toFile(db, "http://...test.jpg", "/www/image.jpg", 0);
*
* @param db Baqend db to use
* @param url the URL that should be downloaded
* @param target the name of the target Baqend file
* @param maxRetries number of retries if the response has a 4xx or 5xx status code
* @returns {Promise} a Promise that resolves to the uploaded file
*/
function toFile(db, url, target, maxRetries = 10) {
return new Promise((resolve, reject) => {
http.get(url, (res) => {
const file = new db.File({path: target});
const size = res.headers['content-length'];
const mimeType = res.headers['content-type'];
//Retry on error
if (res.statusCode >= 400 && res.statusCode < 600) {
if(maxRetries <= 0) {
reject(new Error("Maximum number of retries reached without success"));
} else {
resolve(toFile(db, url, target, maxRetries - 1));
}
return;
}
//Full response
if (size) {
resolve(file.upload({mimeType, size: Number(size), type: 'stream', data: res}));
return;
}
//Or chunked encoding
const chunks = [];
res.on('data', chunks.push.bind(chunks));
res.on('end', () => {
const buf = Buffer.concat(chunks);
resolve(file.upload({mimeType, size: buf.length, type: 'buffer', data: buf, force: true}));
});
}).on('error', (e) => {
reject(e);
});
});
}
exports.toFile = toFile;
To use this module, import it and provide it with the URL to download and the target file path in Baqend. The call will return a Promise resolving to the downloaded Baqend file.
download = require('./download');
download.toFile(db, "http://...test.jpg", "/www/image.jpg");
Downloading Files
Downloading a file works similar to uploading one. Just create a file reference and call file.download()
:
const file = new DB.File({ name: 'myPic.jpg' });
file.download((data) => {
console.log(data); // is provided as Blob per default
// Accessing the metadata of the file
console.log(file.mimeType); // contains the media type of the file
console.log(file.lastModified); // the upload date
console.log(file.eTag); // the eTag of the file
});
To load the file content in a different format, just request a download type
const file = new DB.File({ name: 'myPic.jpg', type: 'data-url' });
file.download((data) => {
// Data is now a data URL string
console.log(data); // "data:image/jpeg;base64,R0lGODlhDAA..."
});
Deleting Files
To delete a file just call the delete()
method after creating the file reference:
const file = new DB.File({ name: 'test.png' });
file.delete().then((file) => {
// Deletion succeed
}, (error) => {
// Upload failed with an error
});
If you try to delete a file and you have previously fetched the file or its metadata and the file has
been changed in the meantime the deletion will be rejected to prevent accidental file deletions.
If you like to skip the verification, you can pass the {force: true}
option to the delete()
call.
File ACLs
The File Permissions works similar to the object acls, you can define permissions on the root folders similar to class-based permissions and file permissions similar to object level permissions.
The root folder permissions are applied to all nesting folders and files.
File Permissions
The following table gives an overview of the required permissions per operation:
Method | Root-folder-based permission | File-based permission |
---|---|---|
.download(), .url |
folder.load | object.acl.read |
.upload(<new file>) |
folder.insert | - |
.upload(<existing file>) |
folder.update | object.acl.write |
.upload({force: true}) |
both folder.insert and folder.update will be checked | object.acl.write |
.delete() |
folder.delete | object.acl.write |
Set root folder Permissions
Per default only the admin can access root folders with one exception. The www
folder is public readable for the file
hosting feature of Baqend.
To change the permissions for a specific root folder yous should commonly use the Baqend Dashboard.
But if you like to change the permissions programmatically you can use the saveMetadata()
method:
// Grant full access on the pictures root folder for the current user
DB.File.saveMetadata('pictures', {
load: new DB.util.Permission().allowAccess(DB.User.me),
insert: new DB.util.Permission().allowAccess(DB.User.me),
update: new DB.util.Permission().allowAccess(DB.User.me),
delete: new DB.util.Permission().allowAccess(DB.User.me),
query: new DB.util.Permission().allowAccess(DB.User.me)
});
Set file Permissions
The file permissions can be set when a file is uploaded. Therefore you can pass the acl option to the File constructor or to the upload method.
const file = new DB.File({
name: 'test.png',
data: file,
acl: new DB.Acl()
.allowReadAccess(DB.User.me)
.allowWriteAccess(DB.User.me)
});
file.upload().then(...);