File Sharing
In addition to messages and signals, PubNub also allows users to upload and share files.
The most common scenarios where you may need to share files are in social apps to share images or in medical apps to share medical records. You can upload and share videos, images, or documents of up to 5 MB in size each.
Configuration
To use File Sharing, you must enable and configure it on a chosen app's keyset in the Admin Portal.
Option | Description |
---|---|
Bucket Region | Geographical region where your file data is stored and managed. |
Retention | How long you'd like to save your files for. For free accounts, you can set it to 1 day or 7 days. Limits are higher for paid accounts. |
Send Files
Sending a file is a lot like publishing a message; you must specify the channel on which to send the file and provide the actual file. You can also optionally include some text to use as a caption for the file.
You can also encrypt files using AES-256 Encryption. To send such files, you must either have a cipher key defined in your initial client configuration or specify the cipher key parameter directly in each request. Other clients will have to provide this key when downloading the file to decrypt the content successfully.
The code below sends the file cat_picture.jpg
of the custom type file-message
on the channel my_channel
. Note that not all SDKs yet support sending messages with a custom message type.
- JavaScript
- C#
- Go
- Objective-C
- Java
- Python
- PHP
- Swift
// web
const input = document.querySelector('input[file]');
input.addEventListener('change', async () => {
const file = input.files[0];
const result = await pubnub.sendFile({
channel: 'my_channel',
file: file,
customMessageType: 'file-message'
});
});
// Node.js
import fs from 'fs';
show all 36 linesPNResult<PNFileUploadResult> fileUploadResponse = await pubnub.SendFile()
.Channel("my_channel")
.File("cat_picture.jpg") // checks the bin folder if no path is provided
.Message("Look at this photo!")
.ExecuteAsync();
PNFileUploadResult fileUploadResult = fileUploadResponse.Result;
PNStatus fileUploadStatus = fileUploadResponse.Status; // contains troubleshooting info
if (!fileUploadStatus.Error && fileUploadResult != null) // checks if successful
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileUploadResult));
}
else
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileUploadStatus));
}
file, err := os.Open("cat_picture.jpg")
defer file.Close()
if err != nil {
panic(err)
}
resSendFile, statusSendFile, errSendFile := pn.SendFile().
Channel("my_channel").
Message("Look at this photo!").
Name("cat_picture.jpg").
File(file).Execute()
fmt.Println(resSendFile, statusSendFile, errSendFile)
fmt.Println(resSendFile.Data.ID)
NSURL *localFileURL = ...;
PNSendFileRequest *request = [PNSendFileRequest requestWithChannel:@"my_channel"
fileURL:localFileURL];
request.customMessageType = @"file-message";
[self.client sendFileWithRequest:request completion:^(PNSendFileStatus *status) {
if (!status.isError) {
/**
* File upload successfully completed.
* Uploaded file information is available here:
* status.data.fileIdentifier is the unique file identifier
* status.data.fileName is the name used to store the file
*/
} else {
/**
show all 23 linespubnub.sendFile()
.channel("my_channel")
.fileName("cat_picture.jpg")
.customMessageType("file-message")
.inputStream(inputStream)
.message("Look at this photo!")
.async(result -> {
result.onSuccess(res -> {
System.out.println("send timetoken: " + res.getTimetoken());
System.out.println("send status: " + res.getStatus());
System.out.println("send fileId: " + res.getFile().getId());
System.out.println("send fileName: " + res.getFile().getName());
}).onFailure(exception -> {
exception.printStackTrace();
});
show all 16 lines# synchronous
with open("cat_picture.jpg", "rb") as fd:
envelope = pubnub.send_file() \
.channel("my_channel") \
.file_name("cat_picture.jpg") \
.message({"test_message": "test"}) \
.custom_message_type('file-message') \
.should_store(True) \
.ttl(222) \
.file_object(fd) \
.cipher_key("secret") \
.sync()
# multithreaded asynchronous
def callback(response, status):
show all 28 linespubnub.sendFile()
->message("Hey, this is the requested file.")
->channel("channel_1")
->fileId("p1n4ppl3p1zz4")
->fileName("pinapplePizza.jpg")
->customMessageType("file-message")
->sync();
pubnub.send(
.file(url: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("cat_picture.jpg")),
channel: "my_channel",
remoteFilename: "cat_picture.jpg",
publishRequest: .init(additionalMessage: ["text": "Look at this photo!"], customMessageType: "file-message")
) { (fileTask) in
print("The task \(fileTask.urlSessionTask.taskIdentifier) has started uploading; no need to call `resume()`")
print("If needed, the `URLSessionUploadTask` can be accessed with `fileTask.urlSessionTask`")
print("You can use `fileTask.progress` to populate a `UIProgressView`/`ProgressView` ")
} completion: { (result) in
switch result {
case let .success((task, file, publishedAt)):
print("The file with an ID of \(file.fileId) was uploaded at \(publishedAt) timetoken) ")
case let .failure(error):
print("An error occurred while uploading the file: \(error.localizedDescription)")
show all 17 linesEven though you upload and share the files directly from the client, it's the storage service that stores and manages the actual files for each key.
Once the file has been uploaded to the storage service, subscribers of a particular channel receive a file message. With this file message (which contains a description, filename, and file ID) you can use our SDKs to generate a URL to display the file or download the file directly. Refer to Retrieve Files for more information.
Enable the Feature
File Sharing is an optional feature that you must enable by turning on the feature for your keys in the Admin Portal.
File Operations
Apart from sending files, you can list all files that were sent to a channel or delete a file that has been previously uploaded to the storage service. Much like with regular messages, you can also search through historical file messages using the Message Persistence API if you enabled Message Persistence on your API key. The information returned by the Message Persistence API allows you to either display the file in the browser or download it.
Refer to the File Sharing section in the SDK documentation to learn more about operations on files.
Receive Files
In order to receive a file, the receiving client should be listening to an event of type file
, and should subscribe to a channel in which the file is being sent. There is no need to subscribe any differently to a channel for receiving a file.
Retrieve Files
Retrieving files is a two-step process accomplished using the History API and File Sharing-related SDK methods. To retrieve a file:
- Get the file message using History API or the
listFiles
method - Display or download the file using dedicated SDK methods based on the obtained file information.
File URLs
Neither the History API nor the listFiles
method returns file URLs. They return file-specific information like the file's name and ID that you need to display or download the file.
When you fetch file messages using History API, the response includes the file details (ID, filename) and a message type of 4
that indicates it's a file message. You can use this information to display or download the file.
Display and Download Files
Apart from the optional description, a file message contains the ID and name of the file.
As a file message does not contain the file URL, you must use one of the available SDK methods to construct the file URL to either display the file or download it.
The following code returns the URL of the cat_picture.jpg
file sent to channel my_channel
:
- JavaScript
- C#
- Go
- Objective-C
- Java
- Python
- Swift
const result = pubnub.getFileUrl({ channel: 'my_channel', id: 'd9515cb7-48a7-41a4-9284-f4bf331bc770', name: 'cat_picture.jpg' });
PNResult<PNFileUrlResult> getFileUrlResponse = await pubnub.GetFileUrl()
.Channel("my_channel")
.FileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
.FileName("cat_picture.jpg")
.ExecuteAsync();
PNFileUrlResult getFileUrlResult = getFileUrlResponse.Result;
PNStatus getFileUrlStatus = getFileUrlResponse.Status;
if (getFileUrlResult != null)
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(getFileUrlResult));
}
else
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(getFileUrlStatus));
}
resGetFile, statusGetFile, errGetFile := pn.GetFileURL().
Channel("my_channel").
ID("d9515cb7-48a7-41a4-9284-f4bf331bc770").
Name("cat_picture.jpg").Execute()
fmt.Println(resGetFile, statusGetFile, errGetFile)
fmt.Println(resGetFile.URL)
NSURL *url = [self.client downloadURLForFileWithName:@"cat_picture.jpg"
identifier:@"<file-identifier>"
inChannel:@"my_channel"];
pubnub.getFileUrl()
.channel("my_channel")
.fileName("cat_picture.jpg")
.fileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
.async(result -> {
result.onSuccess(res -> {
System.out.println("getUrl fileUrl: " + res.getUrl());
}).onFailure(exception -> {
exception.printStackTrace();
});
});
# Synchronous:
envelope = pubnub.get_file_url().
channel("my_channel").
file_id("fileID").
file_name("cat_picture.jpg").sync()
# Multithreaded asynchronous:
def callback(response, status):
pass
pubnub.get_file_url().
channel("my_channel").
file_id("fileID").
file_name("cat_picture.jpg").sync()
do {
let downloadURL = try pubnub.generateFileDownloadURL(channel: "my_channel", fileId: "fileID", filename: "cat_picture.jpg")
} catch {
print("An error occurred generating the URL: \(error.localizedDescription)")
}
The response to this call is the file URL which you can use to display the file in the browser.
You need the same set of file information to download the file. The following code downloads the file cat_picture.jpg
sent to the channel my_channel
:
- JavaScript
- C#
- Go
- Objective-C
- Java
- Python
- Swift
// web
const file = await pubnub.downloadFile({
channel: 'my_channel',
id: '...',
name: 'cat_picture.jpg',
});
const myImageTag = document.createElement('img');
myImageTag.src = URL.createObjectURL(await file.toFile());
document.body.appendChild(myImageTag);
// Node.js using streams
import fs from 'fs'
show all 38 linesPNResult<PNDownloadFileResult> fileDownloadResponse = await pubnub.DownloadFile()
.Channel("my_channel")
.FileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
.FileName("cat_picture.jpg")
.ExecuteAsync();
PNDownloadFileResult fileDownloadResult = fileDownloadResponse.Result;
PNStatus fileDownloadStatus = fileDownloadResponse.Status;
if (fileDownloadResult != null)
{
fileDownloadResult.SaveFileToLocal(downloadUrlFileName); // saves to bin folder if no path is provided
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileDownloadResult.FileName));
}
else
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileDownloadStatus));
show all 16 linesresDLFile, statusDLFile, errDLFile := pn.DownloadFile().
Channel("my_channel").
ID("d9515cb7-48a7-41a4-9284-f4bf331bc770").
Name("cat_picture.jpg").Execute()
if resDLFile != nil {
filepathOutput := "cat_picture.jpg"
out, _ := os.Create(filepathOutput)
_, err := io.Copy(out, resDLFile.File)
if err != nil {
fmt.Println(err)
}
}
PNDownloadFileRequest *request = [PNDownloadFileRequest requestWithChannel:@"my_channel"
identifier:@"<file-identifier>"
name:@"cat_picture.jpg"];
request.targetURL = ...;
[self.client downloadFileWithRequest:request
completion:^(PNDownloadFileResult *result, PNErrorStatus *status) {
if (!status.isError) {
/**
* File successfully has been downloaded.
* status.data.location - location where downloaded file can be found
* status.data.temporary - whether file has been downloaded to temporary storage and
* will be removed on completion block return.
*/
} else {
show all 23 linespubnub.downloadFile()
.channel("my_channel")
.fileName("cat_picture.jpg")
.fileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
.async(result -> {
result.onSuccess(res -> {
System.out.println("getFile fileName: " + res.getFileName());
System.out.println("getFile byteStream: " + res.getByteStream());
}).onFailure(exception -> {
exception.printStackTrace();
});
});
# synchronous:
download_envelope = pubnub.download_file().
channel("my_channel").
file_id("fileId").
file_name("cat_picture.jpg").sync()
# Multithreaded asynchronous:
def callback(response, status):
pass
pubnub.download_file().
channel("my_channel").
file_id("fileID").
file_name("cat_picture.jpg").pn_async(callback)
let requestFile = PubNubLocalFileBase(
fileURL: URL(fileURLWithPath: "cat_picture.jpg"),
channel: "my_channel",
fileId: "fileId",
remoteFilename: "cat_picture.jpg"
)
pubnub.download(
file: requestFile, toFileURL: requestFile.fileURL
) { (fileTask: HTTPFileDownloadTask) in
print("The task \(fileTask.taskIdentifier) has started downloading; no need to call `resume()`")
print("If needed, the `URLSessionUploadTask` can be accessed with `fileTask.urlSessionTask`")
print("You can use `fileTask.progress` to populate a `UIProgressView`/`ProgressView` ")
show all 25 linesList Files
If you're only interested in the actual files and not the file messages (which include the optional description of the uploaded file) that were sent, you can list up to 100 files sent to a particular channel. However, if there are more than 100 files to be returned, the more
flag will be present in the response.
The code below returns the name, ID, size, and created timestamp for up to 100 files sent to the channel my_channel
:
- JavaScript
- C#
- Go
- Objective-C
- Java
- Python
- Swift
const result = await pubnub.listFiles({ channel: 'my_channel' });
PNResult<PNListFilesResult> listFilesResponse = await pubnub.ListFiles()
.Channel("my_channel")
.ExecuteAsync();
PNListFilesResult listFilesResult = listFilesResponse.Result;
PNStatus listFilesStatus = listFilesResponse.Status;
if (listFilesResult != null)
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(listFilesResult));
}
else
{
Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(listFilesStatus));
}
resListFile, statusListFile, errListFile := pn.ListFiles().
Channel("my_channel").Execute()
fmt.Println(resListFile, statusListFile, errListFile)
if resListFile != nil {
for _, m := range resListFile.Data {
fmt.Println(m.ID, m.Created, m.Name, m.Size)
}
}
PNListFilesRequest *request = [PNListFilesRequest requestWithChannel:@"my_channel"];
request.limit = 100;
request.next = ...;
[self.client listFilesWithRequest:request
completion:^(PNListFilesResult *result, PNErrorStatus *status) {
if (!status.isError) {
/**
* Uploaded files list successfully fetched.
* result.data.files - List of uploaded files (information).
* result.data.next - Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off.
* result.data.count - Total number of files uploaded to channel.
*/
} else {
/**
show all 22 linespubnub.listFiles()
.channel("my_channel")
.async(result -> {
result.onSuccess(res -> {
System.out.println("files status: " + res.getStatus());
System.out.println("files status: " + res.getNext());
System.out.println("files status: " + res.getCount());
System.out.println("files status: " + res.getCount());
for (PNUploadedFile file : res.getData()) {
System.out.println("files fileId: " + file.getId());
System.out.println("files fileName: " + file.getName());
System.out.println("files fileSize: " + file.getSize());
System.out.println("files fileCreated: " + file.getCreated());
}
}).onFailure(exception -> {
show all 18 lines# synchronous
pubnub.list_files().channel("my_channel").sync()
# multithreaded asynchronous
def callback(response, status):
pass
pubnub.list_files().channel("my_channel").pn_async(callback)
pubnub.listFiles(
channel: "my_channel"
) { result in
case let .success(response):
print("There are \(response.files.count) file(s) found")
if let nextPage = response.next {
print("The next page used for pagination: \(nextPage)")
}
case let .failure(error):
print("An error occurred while fetching the file list: \(error.localizedDescription)")
}