Stream uploads
For large files or dynamic content generation, use the streaming upload API with uploadWritable(). This gives you direct access to a WritableStream.
import { FTPClient } from "workerd-ftp" ;
const ftp = new FTPClient ( 'ftp.example.com' , {
user: 'username' ,
pass: 'password'
});
await ftp . connect ();
// Get a writable stream
const writable = await ftp . uploadWritable ( 'large-file.bin' , 1024000 );
const writer = writable . getWriter ();
try {
// Write data in chunks
const chunk1 = new TextEncoder (). encode ( 'First chunk of data \n ' );
await writer . write ( chunk1 );
const chunk2 = new TextEncoder (). encode ( 'Second chunk of data \n ' );
await writer . write ( chunk2 );
// Close the writer when done
await writer . close ();
// Finalize the transfer
await ftp . finalizeStream ();
console . log ( 'Streaming upload complete' );
} catch ( error ) {
await writer . abort ();
throw error ;
}
The second parameter to uploadWritable() is optional but some FTP servers require you to allocate space before uploading.
You must call finalizeStream() after finishing with the stream to release the internal lock and close the data connection.
Stream downloads
Download large files efficiently using the streaming API with downloadReadable(). This returns a ReadableStream that you can process incrementally.
import { FTPClient } from "workerd-ftp" ;
const ftp = new FTPClient ( 'ftp.example.com' , {
user: 'username' ,
pass: 'password'
});
await ftp . connect ();
// Get a readable stream
const readable = await ftp . downloadReadable ( 'large-file.bin' );
const reader = readable . getReader ();
try {
while ( true ) {
const { done , value } = await reader . read ();
if ( done ) break ;
// Process chunk
console . log ( `Received ${ value . byteLength } bytes` );
// Process value (Uint8Array) as needed
}
console . log ( 'Download complete' );
} finally {
reader . releaseLock ();
await ftp . finalizeStream ();
}
Upload multiple files
Upload multiple files in sequence. Each operation waits for the previous one to complete.
import { FTPClient } from "workerd-ftp" ;
interface FileToUpload {
name : string ;
content : string ;
}
async function uploadMultipleFiles ( files : FileToUpload []) {
const ftp = new FTPClient ( 'ftp.example.com' , {
user: 'username' ,
pass: 'password'
});
try {
await ftp . connect ();
console . log ( 'Connected to FTP server' );
for ( const file of files ) {
try {
const data = new TextEncoder (). encode ( file . content );
await ftp . upload ( file . name , data );
console . log ( `✓ Uploaded: ${ file . name } ` );
} catch ( error ) {
console . error ( `✗ Failed to upload ${ file . name } :` , error );
// Continue with next file
}
}
console . log ( 'All uploads completed' );
} finally {
await ftp . close ();
}
}
// Usage
const files = [
{ name: 'file1.txt' , content: 'Content of file 1' },
{ name: 'file2.txt' , content: 'Content of file 2' },
{ name: 'file3.txt' , content: 'Content of file 3' }
];
await uploadMultipleFiles ( files );
Download multiple files
Download multiple files and process them as needed.
import { FTPClient } from "workerd-ftp" ;
interface DownloadedFile {
name : string ;
content : string ;
size : number ;
}
async function downloadMultipleFiles ( fileNames : string []) : Promise < DownloadedFile []> {
const ftp = new FTPClient ( 'ftp.example.com' , {
user: 'username' ,
pass: 'password'
});
const results : DownloadedFile [] = [];
try {
await ftp . connect ();
for ( const fileName of fileNames ) {
try {
const data = await ftp . download ( fileName );
const content = new TextDecoder (). decode ( data );
results . push ({
name: fileName ,
content: content ,
size: data . byteLength
});
console . log ( `✓ Downloaded: ${ fileName } ( ${ data . byteLength } bytes)` );
} catch ( error ) {
console . error ( `✗ Failed to download ${ fileName } :` , error );
// Continue with next file
}
}
return results ;
} finally {
await ftp . close ();
}
}
// Usage
const files = await downloadMultipleFiles ([ 'file1.txt' , 'file2.txt' , 'file3.txt' ]);
console . log ( `Downloaded ${ files . length } files` );
Error handling
Implement comprehensive error handling for robust FTP operations.
import { FTPClient } from "workerd-ftp" ;
async function robustFileTransfer ( fileName : string , content : string ) {
const ftp = new FTPClient ( 'ftp.example.com' , {
user: 'username' ,
pass: 'password'
});
try {
// Connect with timeout handling
await ftp . connect ();
} catch ( error : any ) {
if ( error . code ) {
console . error ( `FTP error code ${ error . code } : ${ error . message } ` );
} else {
console . error ( 'Connection failed:' , error );
}
throw error ;
}
try {
// Try to upload the file
const data = new TextEncoder (). encode ( content );
await ftp . upload ( fileName , data );
console . log ( 'Upload successful' );
// Verify the upload
const fileSize = await ftp . size ( fileName );
console . log ( `File size on server: ${ fileSize } bytes` );
if ( fileSize !== data . byteLength ) {
throw new Error ( 'File size mismatch - upload may be corrupted' );
}
} catch ( error : any ) {
if ( error . code === 550 ) {
console . error ( 'File not found or permission denied' );
} else if ( error . code === 426 ) {
console . error ( 'Connection closed; transfer aborted' );
} else if ( error . code === 552 ) {
console . error ( 'Storage allocation exceeded' );
} else {
console . error ( 'Upload error:' , error );
}
throw error ;
} finally {
// Always clean up
await ftp . close ();
}
}
FTP errors include a code property with the standard FTP status code. Common codes include 550 (file unavailable), 426 (connection closed), and 552 (storage exceeded).
Track upload progress
Monitor upload progress using the streaming API.
import { FTPClient } from "workerd-ftp" ;
async function uploadWithProgress ( fileName : string , data : Uint8Array ) {
const ftp = new FTPClient ( 'ftp.example.com' , {
user: 'username' ,
pass: 'password'
});
await ftp . connect ();
const writable = await ftp . uploadWritable ( fileName , data . byteLength );
const writer = writable . getWriter ();
try {
const chunkSize = 64 * 1024 ; // 64 KB chunks
let offset = 0 ;
let bytesUploaded = 0 ;
while ( offset < data . byteLength ) {
const end = Math . min ( offset + chunkSize , data . byteLength );
const chunk = data . slice ( offset , end );
await writer . write ( chunk );
bytesUploaded += chunk . byteLength ;
const progress = ( bytesUploaded / data . byteLength ) * 100 ;
console . log ( `Progress: ${ progress . toFixed ( 1 ) } % ( ${ bytesUploaded } / ${ data . byteLength } bytes)` );
offset = end ;
}
await writer . close ();
await ftp . finalizeStream ();
console . log ( 'Upload complete!' );
} catch ( error ) {
await writer . abort ();
throw error ;
} finally {
await ftp . close ();
}
}
// Usage
const largeContent = new TextEncoder (). encode ( 'Large file content...' );
await uploadWithProgress ( 'large-file.txt' , largeContent );
Binary file handling
The library automatically uses binary mode for all transfers. Here’s how to handle different file types:
Text files
JSON files
Binary files
import { FTPClient } from "workerd-ftp" ;
const ftp = new FTPClient ( 'ftp.example.com' , {
user: 'username' ,
pass: 'password'
});
await ftp . connect ();
// Upload text
const text = 'Hello, World!' ;
await ftp . upload ( 'text.txt' , new TextEncoder (). encode ( text ));
// Download text
const data = await ftp . download ( 'text.txt' );
const downloadedText = new TextDecoder (). decode ( data );