#File Operations
Learn how to manage files with operations like rename, copy, move, and bulk operations.
#Renaming Files
Rename assets to better organize your storage:
import { Walbucket } from '@walbucket/sdk';
const walbucket = new Walbucket({
apiKey: process.env.WALBUCKET_API_KEY!,
network: 'testnet',
sponsorPrivateKey: process.env.SPONSOR_PRIVATE_KEY!,
});
async function renameFile(assetId: string, newName: string) {
try {
await walbucket.rename(assetId, newName);
console.log(`Renamed to: ${newName}`);
} catch (error) {
console.error('Rename failed:', error.message);
}
}
// Simple rename
await renameFile(assetId, 'project-report-final.pdf');typescript
#Batch Rename
Rename multiple files with a pattern:
async function batchRename(assets: any[], pattern: string) {
const results = [];
for (let i = 0; i < assets.length; i++) {
const asset = assets[i];
const newName = pattern
.replace('{index}', String(i + 1).padStart(3, '0'))
.replace('{original}', asset.name)
.replace('{date}', new Date().toISOString().split('T')[0]);
try {
await walbucket.rename(asset.assetId, newName);
results.push({ success: true, assetId: asset.assetId, newName });
console.log(`✓ ${asset.name} → ${newName}`);
} catch (error) {
results.push({
success: false,
assetId: asset.assetId,
error: error.message
});
console.error(`✗ Failed to rename ${asset.name}`);
}
}
return results;
}
// Usage: Rename photos with sequential numbers
const photos = await walbucket.list(userAddress);
await batchRename(photos, 'photo-{index}.jpg');
// Usage: Add date prefix
await batchRename(photos, '{date}-{original}');typescript
#Copying Files
Create duplicates of files:
async function copyFile(assetId: string, newName: string) {
try {
await walbucket.copy(assetId, newName);
console.log(`Created copy: ${newName}`);
} catch (error) {
console.error('Copy failed:', error.message);
}
}
// Create a copy
await copyFile(originalId, 'backup-document.pdf');typescript
#Backup Strategy
Implement automatic backups:
async function createBackup(assetId: string) {
const asset = await walbucket.getAsset(assetId);
if (!asset) {
throw new Error('Asset not found');
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupName = `backup-${timestamp}-${asset.name}`;
await walbucket.copy(assetId, backupName);
console.log(`Backup created: ${backupName}`);
return backupName;
}
// Create backups for important files
const importantFiles = ['doc-id-1', 'doc-id-2', 'doc-id-3'];
for (const fileId of importantFiles) {
await createBackup(fileId);
}typescript
#Versioning System
Implement file versioning:
interface Version {
version: number;
assetId: string;
name: string;
createdAt: number;
}
class FileVersioning {
private versions: Map<string, Version[]> = new Map();
async createVersion(originalId: string, description?: string) {
const asset = await walbucket.getAsset(originalId);
if (!asset) throw new Error('Asset not found');
// Get existing versions
const fileVersions = this.versions.get(originalId) || [];
const nextVersion = fileVersions.length + 1;
// Create versioned name
const baseName = asset.name.replace(/\.[^.]+$/, '');
const extension = asset.name.split('.').pop();
const versionName = `${baseName}-v${nextVersion}${description ? `-${description}` : ''}.${extension}`;
// Copy file with version name
await walbucket.copy(originalId, versionName);
// Track version
const version: Version = {
version: nextVersion,
assetId: originalId, // In practice, get the new asset ID
name: versionName,
createdAt: Date.now(),
};
fileVersions.push(version);
this.versions.set(originalId, fileVersions);
console.log(`Created version ${nextVersion}: ${versionName}`);
return version;
}
getVersionHistory(originalId: string): Version[] {
return this.versions.get(originalId) || [];
}
async restoreVersion(originalId: string, version: number) {
const versions = this.versions.get(originalId);
if (!versions) throw new Error('No versions found');
const targetVersion = versions.find(v => v.version === version);
if (!targetVersion) throw new Error(`Version ${version} not found`);
// Copy the version back as the main file
const asset = await walbucket.getAsset(originalId);
await walbucket.copy(targetVersion.assetId, asset.name);
console.log(`Restored to version ${version}`);
}
}
// Usage
const versioning = new FileVersioning();
// Create versions
await versioning.createVersion(fileId, 'initial-draft');
await versioning.createVersion(fileId, 'review-updates');
await versioning.createVersion(fileId, 'final');
// View history
const history = versioning.getVersionHistory(fileId);
console.log(`File has ${history.length} versions`);
// Restore to previous version
await versioning.restoreVersion(fileId, 2);typescript
#Moving Files
Move files between folders:
async function moveFile(assetId: string, targetFolderId: string) {
try {
await walbucket.moveToFolder(assetId, targetFolderId);
console.log('File moved successfully');
} catch (error) {
console.error('Move failed:', error.message);
}
}
// Move to specific folder
await moveFile(assetId, folderId);
// Move to root
await moveFile(assetId, undefined);typescript
#Bulk Move
Move multiple files at once:
async function bulkMove(assetIds: string[], targetFolderId: string) {
const results = [];
console.log(`Moving ${assetIds.length} files...`);
for (const assetId of assetIds) {
try {
await walbucket.moveToFolder(assetId, targetFolderId);
results.push({ success: true, assetId });
console.log(`✓ Moved: ${assetId}`);
} catch (error) {
results.push({ success: false, assetId, error: error.message });
console.error(`✗ Failed: ${assetId}`);
}
}
const successful = results.filter(r => r.success).length;
console.log(`Moved ${successful}/${assetIds.length} files`);
return results;
}
// Move selected files to archive folder
const selectedFiles = ['id1', 'id2', 'id3'];
await bulkMove(selectedFiles, archiveFolderId);typescript
#Deleting Files
Delete files permanently:
async function deleteFile(assetId: string) {
const confirmed = confirm('Are you sure you want to delete this file?');
if (!confirmed) return;
try {
await walbucket.delete(assetId);
console.log('File deleted');
} catch (error) {
console.error('Delete failed:', error.message);
}
}typescript
#Bulk Delete
Delete multiple files:
async function bulkDelete(assetIds: string[]) {
const confirmed = confirm(
`Delete ${assetIds.length} files? This cannot be undone.`
);
if (!confirmed) return;
const results = [];
for (const assetId of assetIds) {
try {
await walbucket.delete(assetId);
results.push({ success: true, assetId });
console.log(`✓ Deleted: ${assetId}`);
} catch (error) {
results.push({ success: false, assetId, error: error.message });
console.error(`✗ Failed: ${assetId}`);
}
}
const successful = results.filter(r => r.success).length;
console.log(`Deleted ${successful}/${assetIds.length} files`);
return results;
}typescript
#Complete File Manager
Build a complete file management system:
class FileManager {
private walbucket: Walbucket;
constructor(walbucket: Walbucket) {
this.walbucket = walbucket;
}
// Rename with validation
async rename(assetId: string, newName: string): Promise<boolean> {
if (!newName.trim()) {
throw new Error('Name cannot be empty');
}
if (newName.length > 255) {
throw new Error('Name too long (max 255 characters)');
}
try {
await this.walbucket.rename(assetId, newName);
return true;
} catch (error) {
console.error('Rename failed:', error);
return false;
}
}
// Copy with automatic name generation
async copy(assetId: string, baseName?: string): Promise<string | null> {
try {
const asset = await this.walbucket.getAsset(assetId);
if (!asset) throw new Error('Asset not found');
const name = baseName || this.generateCopyName(asset.name);
await this.walbucket.copy(assetId, name);
return name;
} catch (error) {
console.error('Copy failed:', error);
return null;
}
}
// Move with validation
async move(
assetId: string,
targetFolderId: string | null
): Promise<boolean> {
try {
await this.walbucket.moveToFolder(assetId, targetFolderId || undefined);
return true;
} catch (error) {
console.error('Move failed:', error);
return false;
}
}
// Delete with confirmation
async delete(assetId: string, skipConfirm: boolean = false): Promise<boolean> {
if (!skipConfirm) {
const confirmed = confirm('Delete this file permanently?');
if (!confirmed) return false;
}
try {
await this.walbucket.delete(assetId);
return true;
} catch (error) {
console.error('Delete failed:', error);
return false;
}
}
// Batch operations
async batchOperation(
assetIds: string[],
operation: 'delete' | 'move',
options?: { targetFolderId?: string }
) {
const results = [];
for (const assetId of assetIds) {
let success = false;
if (operation === 'delete') {
success = await this.delete(assetId, true);
} else if (operation === 'move') {
success = await this.move(assetId, options?.targetFolderId || null);
}
results.push({ assetId, success });
}
return results;
}
// Helper: Generate copy name
private generateCopyName(originalName: string): string {
const timestamp = Date.now();
const parts = originalName.split('.');
const ext = parts.pop();
const base = parts.join('.');
return `${base}-copy-${timestamp}.${ext}`;
}
}
// Usage
const manager = new FileManager(walbucket);
// Rename
await manager.rename(assetId, 'new-name.pdf');
// Copy
await manager.copy(assetId);
// Move
await manager.move(assetId, folderId);
// Batch delete
await manager.batchOperation(['id1', 'id2', 'id3'], 'delete');
// Batch move
await manager.batchOperation(
['id1', 'id2', 'id3'],
'move',
{ targetFolderId: folderId }
);typescript
#React Component Example
import { useState } from 'react';
import { Walbucket } from '@walbucket/sdk';
function FileOperations({ assetId, asset, walbucket, onUpdate }) {
const [isRenaming, setIsRenaming] = useState(false);
const [newName, setNewName] = useState(asset.name);
async function handleRename() {
try {
await walbucket.rename(assetId, newName);
setIsRenaming(false);
onUpdate();
} catch (error) {
alert('Rename failed: ' + error.message);
}
}
async function handleCopy() {
if (confirm('Create a copy of this file?')) {
try {
const copyName = `Copy of ${asset.name}`;
await walbucket.copy(assetId, copyName);
alert('File copied successfully');
onUpdate();
} catch (error) {
alert('Copy failed: ' + error.message);
}
}
}
async function handleDelete() {
if (confirm('Delete this file permanently?')) {
try {
await walbucket.delete(assetId);
alert('File deleted');
onUpdate();
} catch (error) {
alert('Delete failed: ' + error.message);
}
}
}
return (
<div className="file-operations">
{isRenaming ? (
<div className="rename-form">
<input
value={newName}
onChange={(e) => setNewName(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleRename()}
/>
<button onClick={handleRename}>Save</button>
<button onClick={() => setIsRenaming(false)}>Cancel</button>
</div>
) : (
<div className="file-actions">
<button onClick={() => setIsRenaming(true)}>Rename</button>
<button onClick={handleCopy}>Copy</button>
<button onClick={handleDelete}>Delete</button>
</div>
)}
</div>
);
}typescript
#Best Practices
#Naming Conventions
// Good: Clear, descriptive names
await walbucket.rename(id, 'project-report-2024-Q4.pdf');
await walbucket.rename(id, 'logo-primary-color.png');
// Avoid: Generic or unclear names
// await walbucket.rename(id, 'file1.pdf');
// await walbucket.rename(id, 'untitled.png');typescript
#Error Handling
async function safeRename(assetId: string, newName: string) {
try {
await walbucket.rename(assetId, newName);
return { success: true };
} catch (error) {
if (error.message.includes('E_NOT_OWNER')) {
return { success: false, error: 'Permission denied' };
}
if (error.message.includes('already exists')) {
return { success: false, error: 'Name already in use' };
}
return { success: false, error: 'Operation failed' };
}
}typescript