Upload File / Folder
Upload files or folders to your shard node by sending the file or folder data as the request body with the destination path specified as a query parameter.
POST
/fso
FSO stands for "file system object" - in this case, a file or folder
Important Notes
- • All paths must start with
/(absolute paths only) - • File or folder data is sent as the request body in binary or zip format in case of folders
- • Use your shard node URL and your API key
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| x-user-api-key | string | Yes | Your authentication API key |
| Content-Type | "application/octet-stream" | Optional | MIME type for binary file data |
| Content-Type | "application/zip" | Optional | MIME type for folder uploads using zip files. REQUIRED if a zip folder is being uploaded |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| path | string | Yes | Absolute path where the file or folder should be stored. Must start with '/'. For files, include the filename (e.g., "/folder-1/file-1.txt"). For folders, end with '/' (e.g., "/folder-1/") |
| informProgress | boolean | No | Set to 'true' to receive progress updates (see "Query Progress" section) |
| overwrite | boolean | No | Set to 'true' to overwrite existing files or folders instead of returning 409 error |
Example Request (File Upload)
func UploadFile(localFilePath string, pathOnShard string) {
f, err := os.Open(localFilePath)
if err != nil {
panic(fmt.Errorf("could not open file: %w", err))
}
defer f.Close()
url := fmt.Sprintf("%s/fso?path=%s", SHARD_NODE_URL, url.QueryEscape(pathOnShard))
req, err := http.NewRequest(http.MethodPost, url, f)
if err != nil {
panic(fmt.Errorf("could not create upload request: %w", err))
}
req.Header.Set("x-user-api-key", API_KEY)
req.Header.Set("Content-Type", "application/octet-stream")
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
panic(fmt.Errorf("error uploading file: %w", err))
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
panic(fmt.Sprintf("Upload failed with status %d: %s", resp.StatusCode, string(body)))
}
}
function uploadFile(localPath: string, pathOnShard: string): Promise<void> {
const stats = fs.statSync(localPath);
const stream = fs.createReadStream(localPath);
const url = new URL(`${SHARD_URL}/fso`);
url.searchParams.set('path', pathOnShard);
return new Promise((resolve, reject) => {
const req = http.request({
hostname: url.hostname,
port: url.port,
path: url.pathname + url.search,
method: 'POST',
headers: {
'x-user-api-key': API_KEY,
'Content-Type': 'application/octet-stream',
'Content-Length': stats.size
}
}, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
res.statusCode === 201 ? resolve() : reject(new Error(`${res.statusCode}: ${body}`));
});
});
req.on('error', reject);
stream.on('error', reject);
stream.pipe(req);
});
}
Uploading Folders
You can upload a folder by using zip files. Set the Content-Type header to
application/zip in your request, include the zip file as the request body, and specify a path that ends
with / to denote that this is a folder (e.g., "/folder-1/"). The zip file will be extracted to the
specified path on the shard node. Whatever you had in the zip file will now be present at that path.
Important Note
- Your path must end with a "/" to denote you are uploading a folder
Example Request (Folder Upload)
func UploadFolder(localZipPath string, pathOnShard string) {
f, err := os.Open(localZipPath)
if err != nil {
panic(fmt.Errorf("could not open zip file: %w", err))
}
defer f.Close()
url := fmt.Sprintf("%s/fso?path=%s", SHARD_NODE_URL, url.QueryEscape(pathOnShard))
req, err := http.NewRequest(http.MethodPost, url, f)
if err != nil {
panic(fmt.Errorf("could not create upload request: %w", err))
}
req.Header.Set("x-user-api-key", API_KEY)
req.Header.Set("Content-Type", "application/zip")
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
panic(fmt.Errorf("error uploading folder: %w", err))
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
panic(fmt.Sprintf("Upload failed with status %d: %s", resp.StatusCode, string(body)))
}
}
async function uploadFolder(localZipPath: string, pathOnShard: string): Promise<void> {
const data = await fs.promises.readFile(localZipPath);
const url = `${SHARD_NODE_URL}/fso?path=${encodeURIComponent(pathOnShard)}`;
const res = await fetch(url, {
method: 'POST',
headers: {
'x-user-api-key': API_KEY,
'Content-Type': 'application/zip'
},
body: new Uint8Array(data)
});
if (res.status !== 201) {
const body = await res.text();
throw new Error(`Upload failed with status ${res.status}: ${body}`);
}
}
Response Status Codes
| Code | Status | Description |
|---|---|---|
| 201 | Success | File or folder uploaded successfully |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Invalid or missing API key |
| 404 | Not Found | Destination path doesn't exist |
| 409 | Conflict | File/folder already exists at that path (use overwrite=true to replace) |
| 5xx | Server Error | Internal server error - check response body for details |
Example with Override Parameter
Important Notes
- • When using overwrite=true, ensure enough space for both old and new files until upload completes. Old files aren't deleted until success. You might get an "out of space" error if there's not enough space left
func UploadFileWithOverride(localFilePath string, pathOnShard string) {
f, err := os.Open(localFilePath)
if err != nil {
panic(fmt.Errorf("could not open file: %w", err))
}
defer f.Close()
url := fmt.Sprintf("%s/fso?path=%s&overwrite=true",
SHARD_NODE_URL, url.QueryEscape(pathOnShard))
req, err := http.NewRequest(http.MethodPost, url, f)
if err != nil {
panic(fmt.Errorf("could not create upload request: %w", err))
}
req.Header.Set("x-user-api-key", API_KEY)
req.Header.Set("Content-Type", "application/octet-stream")
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
panic(fmt.Errorf("error uploading file: %w", err))
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
panic(fmt.Sprintf("Upload failed with status %d: %s", resp.StatusCode, string(body)))
}
}
async function uploadFileWithOverride(localFilePath: string, pathOnShard: string): Promise<void>{
const data = await fs.promises.readFile(localFilePath);
const url = `${SHARD_NODE_URL}/fso?path=${encodeURIComponent(pathOnShard)}&overwrite=true`;
const res = await fetch(url, {
method: 'POST',
headers: {
'x-user-api-key': API_KEY,
'Content-Type': 'application/octet-stream'
},
body: new Uint8Array(data)
});
if (res.status !== 201) {
const body = await res.text();
throw new Error(`Upload failed with status ${res.status}: ${body}`);
}
}