Files

You can use Kinvey to store and retrieve binary files of size up to 5TB. Files can be of any format.

The files are automatically enabled for download using a Content Delivery Network (CDN) for massive scale and performance.

Kinvey does not directly serve or accept files. Instead, the Kinvey Files API works by providing a short-lived URL to a third-party cloud storage service from which file(s) can be uploaded or downloaded. Currently, the third-party service used is Google Cloud Storage.

You would typically use the Files API to upload and download:

  • images
  • video files
  • other application-specific files.

When using the library, a file can be represented by:

  • File, Blob, or ArrayBuffer object. This is the recommended way of representing a file.
  • Simple value such as a string.

The library always communicates with Google Cloud Storage using the https protocol. If you want to use http instead, the options argument in the methods described below allow for a tls: false flag.

Recommended tutorials to learn more about uploading files are:

Download

To download a file given its _id, use Kinvey.Files.download. The response contains a string representation of the file content. If you only need to get the file metadata, use Files.find() or consider streaming the file.

var promise = Kinvey.Files.download('file-id')
  .then(function(file) {
    var fileContent = file;
    // ...
  })
  .catch(function(error) {
    // ...
  });

Stream

For web applications, downloading the actual file is not a common use case. Often, just obtaining a download URL is sufficient. This holds for a variety of use cases, including:

  • Prompt a file for download
  • Embed an application-resource, such as a stylesheet
  • Display an image

To stream a file, use Kinvey.Files.stream. The response will contain the file metadata, with the download URL available as _downloadURL property.

var promise = Kinvey.Files.stream('file-id')
  .then(function(file) {
    var url = file._downloadURL;
    // ...
  })
  .catch(function(error) {
    // ...
  });

The most common use case for streaming files is to display an image using <img ... />. See the example below.

// Assume the image variable points to an <img ... /> element.
var image = document.getElementById('myImage');
var promise = Kinvey.Files.stream('file-id')
  .then(function(file) {
    image.setAttribute('src', file._downloadURL);
  })
  .catch(function(error) {
    // ...
  });

Query

To retrieve a list of files, the Querying interface is available for files.

The snippet below retrieves a list of all PNG images. The response is a list of file metadata.

var query = new Kinvey.Query();
query.equalTo('mimeType', 'image/png');
var promise = Kinvey.Files.find(query)
  .then(function(files) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Optionally, you can add the download: true flag to download every file found. The file itself will then become available under the _data property of each file in the list.

Be careful when using the download flag. Every file matching the query will be downloaded regardless of its size, which can lead to significant overhead and performance degradation of your application.

The file metadata returned by any of the methods explained above contains a _downloadURL. Depending on your use case, you might want to re-download a file. The Kinvey.Files.downloadByUrl() method allows you to do just that. The method accepts a _downloadURL.

var promise = Kinvey.Files.download('file-id')
  .then(function(file) {
    // After some time passed, redownload the file.
    return Kinvey.Files.downloadByUrl(file._downloadURL);
  })
  .then(function(fileData) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Specifying a custom expiration time

If you require the temporary download URL to last longer (or shorter) than the default of one hour, you may optionally specify a custom expiration time (in seconds) using the ttl option. If the file is public, the returned download URL never expires.

var promise = Kinvey.Files.download('file-id', {
  ttl: 7200 // Two hours in ms
})
  .then(function(file) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Upload

Use Kinvey.Files.upload() to upload the file and, optionally, save any associated metadata along with it. The example below uploads a file represented as a string. The _id and _filename properties will be auto-generated by Kinvey if left out.

When using custom values for _id, you should avoid values that are exactly 12 or 24 symbols in length. Such values are automatically converted to BSON ObjectID and are not stored as strings in the database, which can interfere with querying and other operations.

To ensure that no items with 12 or 24 symbols long _id are stored in the collection, you can create a pre-save hook that either prevents saving such items, or appends an additional symbol (for example, underscore) to the _id:

if (_id.length === 12 || _id.length === 24) {
  _id += "_";
}

var fileContent = '<file-content>';
var metadata ={
  _id: '<file-id>',
  filename: '<filename>',
  mimeType: '<mime-type>'
};
var promise = Kinvey.Files.upload(fileContent, metadata)
  .then(function(file) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

The snippet below demonstrates how to upload a file through a form. The library attempts to save the file name and type along with the file upload itself. Hence there is no need to manually add any metadata, the library will do that for you.

<form enctype="multipart/form-data" method="post" name="fileUpload">
  <input id="file" multiple type="file" />
  <input type="submit" value="Upload" />
</form>
<script>
  var form = document.getElementsByName('fileUpload')[0];
  form.addEventListener('submit', function(e) {
    e.preventDefault();
    var file = form.getElementById('input')[0].files[0];

    if (file) {
      var promise = Kinvey.Files.upload(file)
        .then(function(file) {
          // ...
        })
        .catch(function(error) {
          // ...
        });
    }
  });
</script>

Resume an Upload

The library supports resumable file uploads with Google Cloud Storage and will attempt to resume a file upload that may have been interrupted by only uploading the remaining portion of a file that has not been received by Google Cloud Storage. The example below shows how to resume a file upload that was started but did not complete.

var fileContent = '<file-content>';
var promise = Kinvey.Files.findById('file-id')
  .catch(function(error) {
    return {
      _id: '<file-id>',
      filename: '<filename>',
      mimeType: '<mime-type>'
    }
  })
  .then(function(metadata) {
    return Kinvey.Files.upload(fileContent, metadata);
  })
  .then(function(file) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Permissions

Files fully support entity level permissions. To upload a file and add read permissions to another user, you can do the following.

var acl = new Kinvey.Acl().addReader('<user-id>');
var fileContent = '<file-content>';
var metadata ={
  _id: '<file-id>',
  _acl: acl.toPlainObject(),
  filename: '<filename>',
  mimeType: '<mime-type>'
};
var promise = Kinvey.Files.upload(fileContent, metadata)
  .then(function(file) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Upload publicly-readable files

By default, files uploaded to Google Cloud Storage through the Kinvey File API are private. This means that every time you wish to download a file, you must first request it via a GET request to Kinvey, which will generate a signed URL that allows download access only for a limited time.

However, using the File API, you may optionally create a publicly-readable file. The download link to this file will be a regular non-signed URL, which will not expire until you delete the file through Kinvey or change the file's status to be private again. To create a publicly-readable file, set the public option to true.

var fileContent = '<file-content>';
var metadata ={
  _id: '<file-id>',
  filename: '<filename>',
  mimeType: '<mime-type>',
  public: true
};
var promise = Kinvey.Files.upload(fileContent, metadata)
  .then(function(file) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Upload Errors

The library uses an exponential backoff strategy if a 5xx error is returned when uploading a file. The default max backoff time is 32 seconds and can be changed by setting options.maxBackoff to a number of seconds.

var fileContent = '<file-content>';
var metadata ={
  _id: '<file-id>',
  filename: '<filename>',
  mimeType: '<mime-type>'
};
var promise = Kinvey.Files.upload(fileContent, metadata, {
  maxBackoff: 10 // 10 seconds
})
  .then(function(file) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

When uploading a large file, the upload request might timeout before the upload is complete. The default timeout is 60000 ms (60 seconds) and can be changed by setting options.timeout to a number of ms.

var fileContent = '<file-content>';
var metadata ={
  _id: '<file-id>',
  filename: '<filename>',
  mimeType: '<mime-type>'
};
var promise = Kinvey.Files.upload(fileContent, metadata, {
  timeout: 5 * 60 * 1000 // 5 minutues
})
  .then(function(file) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Delete

To delete a file, use Kinvey.Files.removeById().

var promise = Kinvey.Files.removeById('file-id')
  .then(function(result) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Linking files to entities in collections

Many common uses for files involve associating them with entities that reside in collections. In order to make this experience as easy as possible, entities in collections may contain KinveyFile references. These references will be automatically resolved when the entity that contains them is retrieved through the Data Store API.

Creating a KinveyFile reference

A reference to a file stored using Kinvey, or a KinveyFile reference, is simply a JSON object with a _type of KinveyFile and an _id containing the ID of a file in Kinvey (as created through the File API).

To store a KinveyFile reference, create or update an entity in any collection with such a property. For example:

var dataStore = Kinvey.DataStore.collection('pets');
dataStore.save({
  dogName: 'Bob',
  furColor: 'brown with black spots',
  pawPrintPicture: {
    _type: 'KinveyFile',
    _id: '325620e4-93dd-4a26-9f84-8a5e62c0db11'
  }
})
  .then(function(pet) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

Please note that only two properties are allowed in a KinveyFile reference: _type and _id. If you attempt to save any additional properties as part of the KinveyFile JSON object, these properties will be ignored by Kinvey and not saved. Any additional properties that you wish to assiciate with the file should be a part of the file itself, and set through the File API.

A KinveyFile is only a reference to a file stored on Kinvey. You are responsible for uploading the file itself using the File API. It is good practice to first upload the file, and only then create the KinveyFile reference.

Downloading using a KinveyFile reference

When querying any collection through the Data Store API, Kinvey will resolve any KinveyFile reference and provide you with a _downloadURL.

var dataStore = Kinvey.DataStore.collection('pets');
dataStore.findById('3f583e9f-d064-4a25-a953-6cf0a3dc2ff1')
  .subscribe(function(pet) {
    /*
      {
        "_id": "3f583e9f-d064-4a25-a953-6cf0a3dc2ff1",
        "_acl": {...},
        "dogName": "Bob",
        "furColor": "brown with black spots",
        "pawPrintPicture": {
          "_type": "KinveyFile",
          "_id": "325620e4-93dd-4a26-9f84-8a5e62c0db11",
          "_filename": "bobsPawPrint.png",
          "_acl": { ... },
          "_downloadURL": <Google Cloud Storage download URL>,
          "_expiresAt": "2018-06-18T23:07:23.394Z"
        }
      }
    */
  }, function(error) {
    // ...
  });

You will then need to download the file directly from Google Cloud Storage by calling Kinvey.Files.downloadByUrl(); with the _downloadURL property of the KinveyFiles reference.

_downloadURL is a temporary URL that will expire one hour after your find() or findById() call is made to Kinvey. While you must begin your file download within that time limit, the download itself may take longer.

Specifying a custom expiration time using KinveyFiles

If you require the temporary download URL to last longer (or shorter) than one hour, you may optionally specify a custom expiration time (in seconds) using the kinveyFileTTL query parameter.

var dataStore = Kinvey.DataStore.collection('pets');
dataStore.findById('3f583e9f-d064-4a25-a953-6cf0a3dc2ff1', { kinveyFileTTL: 3600 })
  .subscribe(function(pet) {
    /*
      {
        "_id": "3f583e9f-d064-4a25-a953-6cf0a3dc2ff1",
        "_acl": {...},
        "dogName": "Bob",
        "furColor": "brown with black spots",
        "pawPrintPicture": {
          "_type": "KinveyFile",
          "_id": "325620e4-93dd-4a26-9f84-8a5e62c0db11",
          "_filename": "bobsPawPrint.png",
          "_acl": { ... },
          "_downloadURL": <Google Cloud Storage download URL>,
          "_expiresAt": "2018-06-18T23:07:23.394Z"
        }
      }
    */
  }, function(error) {
    // ...
  });

If you would like to upload a publicly-readable file that never expires, please refer to the upload publicly-readable files section.

Securely communicating with GCS using KinveyFiles

Secure communication is available as a query parameter when retrieving KinveyFile references through the Data Store API, as well. To use https instead of http, set the kinveyFileTLS parameter to true.

var dataStore = Kinvey.DataStore.collection('pets');
dataStore.findById('3f583e9f-d064-4a25-a953-6cf0a3dc2ff1', { kinveyFileTLS: true })
  .subscribe(function(pet) {
    /*
      {
        "_id": "3f583e9f-d064-4a25-a953-6cf0a3dc2ff1",
        "_acl": {...},
        "dogName": "Bob",
        "furColor": "brown with black spots",
        "pawPrintPicture": {
          "_type": "KinveyFile",
          "_id": "325620e4-93dd-4a26-9f84-8a5e62c0db11",
          "_filename": "bobsPawPrint.png",
          "_acl": { ... },
          "_downloadURL": <Google Cloud Storage download URL>,
          "_expiresAt": "2018-06-18T23:07:23.394Z"
        }
      }
    */
  }, function(error) {
    // ...
  });

Business Logic

You may create Business Logic code which will execute before or after each File API v3 route. To do so, simply add hooks to the _blob collection just like any other data collection. For more information on Business Logic, refer to the Business Logic guide.

Please note that Business Logic is only supported for File API version 3 routes. If you attempt to access a File API version 2 route that includes Business Logic, your request will fail.

Comprehensive reference

Properties

While you may include any number of custom metadata properties when saving your file, Kinvey uses certain properties to maintain its own metadata. This section contains a comprehensive list of all special Kinvey attributes that may be part of a file.

PropertyShort description
_idA unique identifier
_filenameA filename
_aclThe file's Access Control List
_kmdKinvey metadata, including lmt and ect
_publicIf set to true, the file is publicly accessible through an unsigned URL. Defaults to false
_downloadURLA link to GET the file
_expiresAtThe time at which the link will expire. Will not be included if the file's _public attribute is set to true

Special supported properties

The following properties, while optional, will allow the Kinvey Console to display more information about the file if you specify them as part of the file metadata.

PropertyShort description
mimeTypeThe mime type of the file (e.g. text/plain, image/png, etc)

Optional query parameters

ParameterApplies toDefaultShort description
ttlDownloading3600If set, Google Cloud Storage URLs will expire after the specified amount of time
tlsDownloading and UploadingfalseIf true, Google Cloud Storage URLs will use the https protocol instead of http
kinveyFileTTLKinveyFiles3600If set, Google Cloud Storage URLs will expire after the specified amount of time
kinveyFileTLSKinveyFilesfalseIf true, Google Cloud Storage URLs will use the https protocol instead of http