src/platform/plugins/shared/files/docs/tutorial.mdx
Blobs include any of the following:
When storing data in Elasticsearch, the file service uses a system index. System indices have strict access controls so most users will not be able to access documents with blob contents directly. However, plugins are able to expose file contents over HTTP (more on this later) and there is nothing preventing a plugin from bypassing regular security checks which may unintentionally expose files.
To ensure the most secure file storage experience:
Files are a composition of Saved Objects (SOs) and blobs. SOs contain all metadata needed to provide file service functionality.
The file blob, or file content, is stored separately. When creating a file we first need to create the file metadata, which acts as an upload target, and then upload file content.
<DocCallOut title="File contents are immutable"> Once uploaded, the contents of a file do not change. Browsers, and other clients, should only need to download the contents of a file once, subsequent downloads should be served from the cache.If you need to update the contents of a file, you should create a new file with new contents. </DocCallOut>
Consumers of the file service are able to decide what metadata they want to associate with a file. The file service provides the ability to search custom data which is useful for tagging and filtering your files — all metadata must be serializable to JSON. Learn more about how to specify custom metadata below.
All setup examples are based on the filesExample plugin.
First add the files plugin as a required dependency in your kibana.json:
"requiredPlugins": ["files"],
Your plugin can have more than one file kind. Each file kind should represent a specific use case, for example: an image for user avatars.
import { FileKind } from '@kbn/files-plugin/common';
export const PLUGIN_ID = 'filesExample';
const httpTags = {
requiredPrivileges: [PLUGIN_ID], // ensure that only users with the specified privilege can perform operations on files of this kind
};
export const exampleFileKind: FileKind = {
id: 'filesExample',
maxSizeBytes: 8 * 1024 * 1024, // 8 MiB
http: {
create: httpTags,
delete: httpTags,
download: httpTags,
getById: httpTags,
list: httpTags,
// share: httpTags,
// update: httpTags,
},
};
In this example we have chosen to disallow metadata updates and file sharing endpoints. Make sure that you only expose the actions you need over HTTP.
<DocCallOut title="Ensure users can access their files"> HTTP access tags (`access:myApp`) define a new permission for an HTTP endpoint. **Ensure that users have the permissions they need to access their files!** See `KibanaFeatureConfig` for more information. </DocCallOut>Now we are able to register this file kind with the file service:
// in your server-side and client-side plugin code
public setup(core: CoreSetup, { files }: { files: FilesSetup }) {
files.registerFileKind(exampleFileKind);
}
Let's use the file client now that we have all the boilerplate out of the way:
// in your public code
const client = files.filesClientFactory.asScoped('filesExample'); // Create a file client that is scoped to "filesExample"
const result = await client.list();
Note: the server-side file client has unrestricted access to all files because it does not need to pass through HTTP API access control!
Right now this list will be empty... How do we get our image avatars into the file service? By asking users!
The file service provides a set of React UI components for you to leverage. Users can give you a new file (UploadFile) or select a from a set of files they have already uploaded (FilePicker).
Let's go with the latter for now because users can pick from previously uploaded avatars or provide a new one.
const client = files.filesClientFactory.asUnscoped(); // Create a client not scoped to a file kind
...
<FilesContext client={client}>
<FilePicker kind="filesExample" ... />
</FilesContext>
...
You'll notice the FilesContext component was introduced here. This provides
access to our registry of file kinds among other things so make sure your app is
wrapped in this context.
At this point you can use the public components provided by the file service to render an image in the browser:
const client = files.filesClientFactory.asUnscoped(); // Create a client not scoped to a file kind
...
<Image src={client.getDownloadHref({ id: '<file-id>', kind: 'filesExample' })} alt="..." />
...
Alternatively, you can use the file client directly to create a new file from the browser:
const { file } = await files.example.create({
name: 'my_file',
alt: 'My image',
/**
* How you define metadata is entirely up to you. Just make sure that your application
* remains compatible with previous versions of your metadata.
*/
meta: { myValue: 'test' } as MyMetadata,
mimeType: 'image/png',
});
// ...
await files.example.upload({ id: file.id, body: blob });
After completing this tutorial you should have successfully integrated your application with the file service: