node-appwrite
Version:
Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API
664 lines (549 loc) • 20.8 kB
JavaScript
const Service = require('../service.js');
const AppwriteException = require('../exception.js');
const InputFile = require('../inputFile.js');
const client = require('../client.js');
const Stream = require('stream');
const { promisify } = require('util');
const fs = require('fs');
const { File } = require('undici');
const Query = require('../query.js');
class Storage extends Service {
constructor(client)
{
super(client);
}
/**
* List buckets
*
* Get a list of all the storage buckets. You can use the query params to
* filter your results.
*
* @param {string[]} queries
* @param {string} search
* @throws {AppwriteException}
* @returns {Promise}
*/
async listBuckets(queries, search) {
const apiPath = '/storage/buckets';
let payload = {};
if (typeof queries !== 'undefined') {
payload['queries'] = queries;
}
if (typeof search !== 'undefined') {
payload['search'] = search;
}
return await this.client.call('get', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* Create bucket
*
* Create a new storage bucket.
*
* @param {string} bucketId
* @param {string} name
* @param {string[]} permissions
* @param {boolean} fileSecurity
* @param {boolean} enabled
* @param {number} maximumFileSize
* @param {string[]} allowedFileExtensions
* @param {Compression} compression
* @param {boolean} encryption
* @param {boolean} antivirus
* @throws {AppwriteException}
* @returns {Promise}
*/
async createBucket(bucketId, name, permissions, fileSecurity, enabled, maximumFileSize, allowedFileExtensions, compression, encryption, antivirus) {
const apiPath = '/storage/buckets';
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof name === 'undefined') {
throw new AppwriteException('Missing required parameter: "name"');
}
if (typeof bucketId !== 'undefined') {
payload['bucketId'] = bucketId;
}
if (typeof name !== 'undefined') {
payload['name'] = name;
}
if (typeof permissions !== 'undefined') {
payload['permissions'] = permissions;
}
if (typeof fileSecurity !== 'undefined') {
payload['fileSecurity'] = fileSecurity;
}
if (typeof enabled !== 'undefined') {
payload['enabled'] = enabled;
}
if (typeof maximumFileSize !== 'undefined') {
payload['maximumFileSize'] = maximumFileSize;
}
if (typeof allowedFileExtensions !== 'undefined') {
payload['allowedFileExtensions'] = allowedFileExtensions;
}
if (typeof compression !== 'undefined') {
payload['compression'] = compression;
}
if (typeof encryption !== 'undefined') {
payload['encryption'] = encryption;
}
if (typeof antivirus !== 'undefined') {
payload['antivirus'] = antivirus;
}
return await this.client.call('post', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* Get bucket
*
* Get a storage bucket by its unique ID. This endpoint response returns a
* JSON object with the storage bucket metadata.
*
* @param {string} bucketId
* @throws {AppwriteException}
* @returns {Promise}
*/
async getBucket(bucketId) {
const apiPath = '/storage/buckets/{bucketId}'.replace('{bucketId}', bucketId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
return await this.client.call('get', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* Update bucket
*
* Update a storage bucket by its unique ID.
*
* @param {string} bucketId
* @param {string} name
* @param {string[]} permissions
* @param {boolean} fileSecurity
* @param {boolean} enabled
* @param {number} maximumFileSize
* @param {string[]} allowedFileExtensions
* @param {Compression} compression
* @param {boolean} encryption
* @param {boolean} antivirus
* @throws {AppwriteException}
* @returns {Promise}
*/
async updateBucket(bucketId, name, permissions, fileSecurity, enabled, maximumFileSize, allowedFileExtensions, compression, encryption, antivirus) {
const apiPath = '/storage/buckets/{bucketId}'.replace('{bucketId}', bucketId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof name === 'undefined') {
throw new AppwriteException('Missing required parameter: "name"');
}
if (typeof name !== 'undefined') {
payload['name'] = name;
}
if (typeof permissions !== 'undefined') {
payload['permissions'] = permissions;
}
if (typeof fileSecurity !== 'undefined') {
payload['fileSecurity'] = fileSecurity;
}
if (typeof enabled !== 'undefined') {
payload['enabled'] = enabled;
}
if (typeof maximumFileSize !== 'undefined') {
payload['maximumFileSize'] = maximumFileSize;
}
if (typeof allowedFileExtensions !== 'undefined') {
payload['allowedFileExtensions'] = allowedFileExtensions;
}
if (typeof compression !== 'undefined') {
payload['compression'] = compression;
}
if (typeof encryption !== 'undefined') {
payload['encryption'] = encryption;
}
if (typeof antivirus !== 'undefined') {
payload['antivirus'] = antivirus;
}
return await this.client.call('put', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* Delete bucket
*
* Delete a storage bucket by its unique ID.
*
* @param {string} bucketId
* @throws {AppwriteException}
* @returns {Promise}
*/
async deleteBucket(bucketId) {
const apiPath = '/storage/buckets/{bucketId}'.replace('{bucketId}', bucketId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
return await this.client.call('delete', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* List files
*
* Get a list of all the user files. You can use the query params to filter
* your results.
*
* @param {string} bucketId
* @param {string[]} queries
* @param {string} search
* @throws {AppwriteException}
* @returns {Promise}
*/
async listFiles(bucketId, queries, search) {
const apiPath = '/storage/buckets/{bucketId}/files'.replace('{bucketId}', bucketId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof queries !== 'undefined') {
payload['queries'] = queries;
}
if (typeof search !== 'undefined') {
payload['search'] = search;
}
return await this.client.call('get', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* Create file
*
* Create a new file. Before using this route, you should create a new bucket
* resource using either a [server
* integration](https://appwrite.io/docs/server/storage#storageCreateBucket)
* API or directly from your Appwrite console.
*
* Larger files should be uploaded using multiple requests with the
* [content-range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range)
* header to send a partial request with a maximum supported chunk of `5MB`.
* The `content-range` header values should always be in bytes.
*
* When the first request is sent, the server will return the **File** object,
* and the subsequent part request must include the file's **id** in
* `x-appwrite-id` header to allow the server to know that the partial upload
* is for the existing file and not for a new one.
*
* If you're creating a new file using one of the Appwrite SDKs, all the
* chunking logic will be managed by the SDK internally.
*
*
* @param {string} bucketId
* @param {string} fileId
* @param {InputFile} file
* @param {string[]} permissions
* @param {CallableFunction} onProgress
* @throws {AppwriteException}
* @returns {Promise}
*/
async createFile(bucketId, fileId, file, permissions, onProgress = () => {}) {
const apiPath = '/storage/buckets/{bucketId}/files'.replace('{bucketId}', bucketId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof fileId === 'undefined') {
throw new AppwriteException('Missing required parameter: "fileId"');
}
if (typeof file === 'undefined') {
throw new AppwriteException('Missing required parameter: "file"');
}
if (typeof fileId !== 'undefined') {
payload['fileId'] = fileId;
}
if (typeof file !== 'undefined') {
payload['file'] = file;
}
if (typeof permissions !== 'undefined') {
payload['permissions'] = permissions;
}
const size = file.size;
const apiHeaders = {
'content-type': 'multipart/form-data',
};
let id = undefined;
let response = undefined;
let chunksUploaded = 0;
if(fileId != 'unique()') {
try {
response = await this.client.call('get', apiPath + '/' + fileId, apiHeaders);
chunksUploaded = response.chunksUploaded;
} catch(e) {
}
}
let currentChunk = 1;
let currentPosition = 0;
let uploadableChunk = new Uint8Array(client.CHUNK_SIZE);
const uploadChunk = async (lastUpload = false) => {
if(currentChunk <= chunksUploaded) {
return;
}
const start = ((currentChunk - 1) * client.CHUNK_SIZE);
let end = start + currentPosition - 1;
if(!lastUpload || currentChunk !== 1) {
apiHeaders['content-range'] = 'bytes ' + start + '-' + end + '/' + size;
}
let uploadableChunkTrimmed;
if(currentPosition + 1 >= client.CHUNK_SIZE) {
uploadableChunkTrimmed = uploadableChunk;
} else {
uploadableChunkTrimmed = new Uint8Array(currentPosition);
for(let i = 0; i <= currentPosition; i++) {
uploadableChunkTrimmed[i] = uploadableChunk[i];
}
}
if (id) {
apiHeaders['x-appwrite-id'] = id;
}
payload['file'] = { type: 'file', file: new File([uploadableChunkTrimmed], file.filename), filename: file.filename };
response = await this.client.call('post', apiPath, apiHeaders, payload);
if (!id) {
id = response['$id'];
}
if (onProgress !== null) {
onProgress({
$id: response['$id'],
progress: Math.min((currentChunk) * client.CHUNK_SIZE, size) / size * 100,
sizeUploaded: end+1,
chunksTotal: response['chunksTotal'],
chunksUploaded: response['chunksUploaded']
});
}
uploadableChunk = new Uint8Array(client.CHUNK_SIZE);
currentChunk++;
currentPosition = 0;
}
for await (const chunk of file.stream) {
for(const b of chunk) {
uploadableChunk[currentPosition] = b;
currentPosition++;
if(currentPosition >= client.CHUNK_SIZE) {
await uploadChunk();
currentPosition = 0;
}
}
}
if (currentPosition > 0) { // Check if there's any remaining data for the last chunk
await uploadChunk(true);
}
return response;
}
/**
* Get file
*
* Get a file by its unique ID. This endpoint response returns a JSON object
* with the file metadata.
*
* @param {string} bucketId
* @param {string} fileId
* @throws {AppwriteException}
* @returns {Promise}
*/
async getFile(bucketId, fileId) {
const apiPath = '/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}', bucketId).replace('{fileId}', fileId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof fileId === 'undefined') {
throw new AppwriteException('Missing required parameter: "fileId"');
}
return await this.client.call('get', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* Update file
*
* Update a file by its unique ID. Only users with write permissions have
* access to update this resource.
*
* @param {string} bucketId
* @param {string} fileId
* @param {string} name
* @param {string[]} permissions
* @throws {AppwriteException}
* @returns {Promise}
*/
async updateFile(bucketId, fileId, name, permissions) {
const apiPath = '/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}', bucketId).replace('{fileId}', fileId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof fileId === 'undefined') {
throw new AppwriteException('Missing required parameter: "fileId"');
}
if (typeof name !== 'undefined') {
payload['name'] = name;
}
if (typeof permissions !== 'undefined') {
payload['permissions'] = permissions;
}
return await this.client.call('put', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* Delete File
*
* Delete a file by its unique ID. Only users with write permissions have
* access to delete this resource.
*
* @param {string} bucketId
* @param {string} fileId
* @throws {AppwriteException}
* @returns {Promise}
*/
async deleteFile(bucketId, fileId) {
const apiPath = '/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}', bucketId).replace('{fileId}', fileId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof fileId === 'undefined') {
throw new AppwriteException('Missing required parameter: "fileId"');
}
return await this.client.call('delete', apiPath, {
'content-type': 'application/json',
}, payload);
}
/**
* Get file for download
*
* Get a file content by its unique ID. The endpoint response return with a
* 'Content-Disposition: attachment' header that tells the browser to start
* downloading the file to user downloads directory.
*
* @param {string} bucketId
* @param {string} fileId
* @throws {AppwriteException}
* @returns {Promise}
*/
async getFileDownload(bucketId, fileId) {
const apiPath = '/storage/buckets/{bucketId}/files/{fileId}/download'.replace('{bucketId}', bucketId).replace('{fileId}', fileId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof fileId === 'undefined') {
throw new AppwriteException('Missing required parameter: "fileId"');
}
return await this.client.call('get', apiPath, {
'content-type': 'application/json',
}, payload, 'arraybuffer');
}
/**
* Get file preview
*
* Get a file preview image. Currently, this method supports preview for image
* files (jpg, png, and gif), other supported formats, like pdf, docs, slides,
* and spreadsheets, will return the file icon image. You can also pass query
* string arguments for cutting and resizing your preview image. Preview is
* supported only for image files smaller than 10MB.
*
* @param {string} bucketId
* @param {string} fileId
* @param {number} width
* @param {number} height
* @param {ImageGravity} gravity
* @param {number} quality
* @param {number} borderWidth
* @param {string} borderColor
* @param {number} borderRadius
* @param {number} opacity
* @param {number} rotation
* @param {string} background
* @param {ImageFormat} output
* @throws {AppwriteException}
* @returns {Promise}
*/
async getFilePreview(bucketId, fileId, width, height, gravity, quality, borderWidth, borderColor, borderRadius, opacity, rotation, background, output) {
const apiPath = '/storage/buckets/{bucketId}/files/{fileId}/preview'.replace('{bucketId}', bucketId).replace('{fileId}', fileId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof fileId === 'undefined') {
throw new AppwriteException('Missing required parameter: "fileId"');
}
if (typeof width !== 'undefined') {
payload['width'] = width;
}
if (typeof height !== 'undefined') {
payload['height'] = height;
}
if (typeof gravity !== 'undefined') {
payload['gravity'] = gravity;
}
if (typeof quality !== 'undefined') {
payload['quality'] = quality;
}
if (typeof borderWidth !== 'undefined') {
payload['borderWidth'] = borderWidth;
}
if (typeof borderColor !== 'undefined') {
payload['borderColor'] = borderColor;
}
if (typeof borderRadius !== 'undefined') {
payload['borderRadius'] = borderRadius;
}
if (typeof opacity !== 'undefined') {
payload['opacity'] = opacity;
}
if (typeof rotation !== 'undefined') {
payload['rotation'] = rotation;
}
if (typeof background !== 'undefined') {
payload['background'] = background;
}
if (typeof output !== 'undefined') {
payload['output'] = output;
}
return await this.client.call('get', apiPath, {
'content-type': 'application/json',
}, payload, 'arraybuffer');
}
/**
* Get file for view
*
* Get a file content by its unique ID. This endpoint is similar to the
* download method but returns with no 'Content-Disposition: attachment'
* header.
*
* @param {string} bucketId
* @param {string} fileId
* @throws {AppwriteException}
* @returns {Promise}
*/
async getFileView(bucketId, fileId) {
const apiPath = '/storage/buckets/{bucketId}/files/{fileId}/view'.replace('{bucketId}', bucketId).replace('{fileId}', fileId);
let payload = {};
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
if (typeof fileId === 'undefined') {
throw new AppwriteException('Missing required parameter: "fileId"');
}
return await this.client.call('get', apiPath, {
'content-type': 'application/json',
}, payload, 'arraybuffer');
}
}
module.exports = Storage;