Upload, download, and manipulate files with comprehensive file operations
Apisto provides extensive file handling capabilities including uploads, downloads, format conversions, and progress tracking for all file types.
💡 File Support: Apisto handles images, documents, videos, and any file type with automatic MIME type detection and proper Content-Type headers.
Download files in various formats with automatic type detection and conversion.
// Get file as blob with metadata
const fileInfo = await api.getFile('/documents/report.pdf');
console.log('File info:', {
filename: fileInfo.filename,
contentType: fileInfo.contentType,
size: fileInfo.size,
url: fileInfo.url // Object URL for immediate use
});
// Use the file directly
const link = document.createElement('a');
link.href = fileInfo.url;
link.download = fileInfo.filename;
link.click();
// Clean up object URL
URL.revokeObjectURL(fileInfo.url);
// Download as different formats
const imageBlob = await api.getFileAsBlob('/images/photo.jpg');
const imageBase64 = await api.getFileAsBase64('/images/photo.jpg');
const textContent = await api.getFileAsText('/documents/readme.txt');
const binaryBuffer = await api.getFileAsBuffer('/files/data.bin');
// Use base64 images directly
document.getElementById('avatar').src = await api.getFileAsBase64('/images/avatar.jpg');
// Process text files
const csvText = await api.getFileAsText('/reports/data.csv');
const csvData = Papa.parse(csvText); // Parse CSV
// Handle binary data
const buffer = await api.getFileAsBuffer('/files/archive.zip');
const uint8Array = new Uint8Array(buffer);
class FileDownloader {
constructor(api) {
this.api = api;
}
async downloadAndProcess(url, options = {}) {
const fileInfo = await this.api.getFile(url);
// Process based on file type
switch (fileInfo.contentType) {
case 'application/pdf':
return await this.processPDF(fileInfo);
case 'image/jpeg':
case 'image/png':
return await this.processImage(fileInfo);
case 'text/csv':
return await this.processCSV(fileInfo);
default:
return fileInfo;
}
}
async processPDF(fileInfo) {
// PDF.js or similar processing
console.log('Processing PDF:', fileInfo.filename);
return {
type: 'pdf',
pages: 'unknown', // Would use PDF.js to get page count
...fileInfo
};
}
async processImage(fileInfo) {
// Create thumbnail or process image
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
resolve({
type: 'image',
width: img.width,
height: img.height,
aspectRatio: img.width / img.height,
...fileInfo
});
};
img.src = fileInfo.url;
});
}
async processCSV(fileInfo) {
const text = await this.api.getFileAsText(fileInfo.url);
const data = Papa.parse(text, { header: true });
return {
type: 'csv',
rows: data.data.length,
columns: data.meta.fields,
data: data.data,
...fileInfo
};
}
}
// Usage
const downloader = new FileDownloader(api);
const processedFile = await downloader.downloadAndProcess('/reports/data.csv');
console.log('Processed file:', processedFile);
Upload single or multiple files with progress tracking and automatic FormData handling.
// Basic file upload
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const result = await api.postFile('/upload', file, {
description: 'User profile picture',
category: 'avatars'
});
// Upload with additional fields
const uploadResult = await api.postFile('/documents', file, {
title: 'Project Report',
tags: ['important', 'q1-2024'],
visibility: 'private',
uploadedBy: 'user123'
});
// Using different HTTP methods
await api.putFile('/files/123', file, {
reason: 'updated_version'
});
await api.patchFile('/files/123', null, {
description: 'New description'
}); // No file, just metadata
// Upload multiple files at once
const fileInput = document.getElementById('multiFileInput');
const files = Array.from(fileInput.files);
const result = await api.postFile('/uploads/batch', files, {
album: 'Vacation Photos',
description: 'Summer 2024 collection',
category: 'personal'
});
// Upload files with different field names
await api.postFile('/user/profile', {
avatar: document.getElementById('avatar').files[0],
coverPhoto: document.getElementById('cover').files[0],
documents: document.getElementById('docs').files[0]
}, {
userId: '123',
updateType: 'full_profile'
});
// Using uploadMultiple method for more control
await api.uploadMultiple('/gallery/upload', files, 'POST', {
galleryId: 'gallery-123',
public: false
}, {
onProgress: (progress) => {
console.log(`Upload progress: ${progress.percentage}%`);
}
});
Monitor upload and download progress with real-time updates and speed calculation.
// Upload with progress tracking
async function uploadWithProgress(file, onProgress) {
try {
const result = await api.postFile('/upload', file, {}, {
onProgress: (progress) => {
console.log(`Upload: ${progress.percentage.toFixed(1)}%`);
console.log(`Speed: ${(progress.speed / 1024 / 1024).toFixed(2)} MB/s`);
// Update UI
updateProgressBar(progress.percentage);
updateSpeedDisplay(progress.speed);
if (onProgress) onProgress(progress);
}
});
console.log('Upload completed:', result);
return result;
} catch (error) {
console.error('Upload failed:', error);
throw error;
}
}
// UI integration example
function createUploadUI() {
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
const speedText = document.getElementById('speedText');
const cancelButton = document.getElementById('cancelButton');
let currentUpload = null;
cancelButton.addEventListener('click', () => {
if (currentUpload) {
currentUpload.cancel();
}
});
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const file = document.getElementById('fileInput').files[0];
if (!file) return;
try {
currentUpload = await api.upload.upload('/files', file, {}, {
onProgress: (progress) => {
progressBar.style.width = `${progress.percentage}%`;
progressText.textContent = `${progress.percentage.toFixed(1)}%`;
speedText.textContent = `${(progress.speed / 1024 / 1024).toFixed(2)} MB/s`;
// Change color when complete
if (progress.percentage === 100) {
progressBar.classList.add('bg-green-500');
}
}
});
const result = await currentUpload.promise;
console.log('Upload successful:', result);
showSuccessMessage('File uploaded successfully!');
} catch (error) {
if (error.code === 'UPLOAD_CANCELLED') {
showErrorMessage('Upload cancelled by user');
} else {
showErrorMessage('Upload failed: ' + error.message);
}
} finally {
currentUpload = null;
}
});
}
// Format speed for display
function formatSpeed(bytesPerSecond) {
if (bytesPerSecond < 1024) {
return `${bytesPerSecond.toFixed(0)} B/s`;
} else if (bytesPerSecond < 1024 * 1024) {
return `${(bytesPerSecond / 1024).toFixed(1)} KB/s`;
} else {
return `${(bytesPerSecond / 1024 / 1024).toFixed(1)} MB/s`;
}
}
Upload large files in manageable chunks with resume capabilities and error recovery.
// Chunked upload for large files
async function uploadLargeFile(largeFile, onProgress) {
console.log(`Starting chunked upload for: ${largeFile.name} (${(largeFile.size / 1024 / 1024).toFixed(2)} MB)`);
const upload = await api.upload.uploadChunked('/files/large', largeFile, {
chunkSize: 5 * 1024 * 1024, // 5MB chunks
fields: {
description: '4K Video File',
category: 'videos',
originalName: largeFile.name
},
finalizeEndpoint: '/files/finalize', // Called when all chunks are uploaded
maxChunkRetries: 3,
onProgress: (progress) => {
const details = `
Overall: ${progress.percentage.toFixed(1)}%
Chunk ${progress.chunk}/${progress.totalChunks}: ${progress.chunkProgress.toFixed(1)}%
Speed: ${formatSpeed(progress.bytesPerSecond)}
`.trim();
console.log(details);
if (onProgress) {
onProgress({
...progress,
details: `Chunk ${progress.chunk}/${progress.totalChunks}`
});
}
}
});
// You can control the upload
// upload.pause(); // Pause the entire upload
// upload.resume(); // Resume from where it left off
// upload.cancel(); // Cancel the upload
// upload.retryChunk(5); // Retry specific chunk
try {
const result = await upload.promise;
console.log('Chunked upload completed:', result);
return result;
} catch (error) {
console.error('Chunked upload failed:', error);
throw error;
}
}
// Resume functionality
class ResumableUpload {
constructor(api, file, endpoint) {
this.api = api;
this.file = file;
this.endpoint = endpoint;
this.uploadId = null;
this.uploadedChunks = new Set();
}
async startOrResume(onProgress) {
// Check if there's an existing upload to resume
const existingUpload = await this.checkExistingUpload();
if (existingUpload) {
console.log('Resuming existing upload:', existingUpload.uploadId);
this.uploadId = existingUpload.uploadId;
this.uploadedChunks = new Set(existingUpload.uploadedChunks);
}
return await this.api.upload.uploadChunked(this.endpoint, this.file, {
uploadId: this.uploadId,
onProgress,
// ... other options
});
}
async checkExistingUpload() {
try {
// Implementation depends on your backend
const status = await this.api.get(`/uploads/status?file=${this.file.name}`);
return status;
} catch (error) {
return null;
}
}
}
Validate files before upload with size limits, type restrictions, and security checks.
class FileValidator {
static validateFile(file, rules = {}) {
const {
maxSize = 10 * 1024 * 1024, // 10MB default
allowedTypes = [],
allowedExtensions = [],
maxDimensions = null // for images
} = rules;
const errors = [];
// Size validation
if (file.size > maxSize) {
errors.push(`File too large: ${(file.size / 1024 / 1024).toFixed(2)}MB (max: ${maxSize / 1024 / 1024}MB)`);
}
// Type validation
if (allowedTypes.length > 0 && !allowedTypes.includes(file.type)) {
errors.push(`File type not allowed: ${file.type}`);
}
// Extension validation
if (allowedExtensions.length > 0) {
const extension = file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(extension)) {
errors.push(`File extension not allowed: .${extension}`);
}
}
// Image dimension validation (async)
if (maxDimensions && file.type.startsWith('image/')) {
return this.validateImageDimensions(file, maxDimensions)
.then(dimensionErrors => [...errors, ...dimensionErrors]);
}
return Promise.resolve(errors);
}
static validateImageDimensions(file, maxDimensions) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
const errors = [];
if (img.width > maxDimensions.width) {
errors.push(`Image too wide: ${img.width}px (max: ${maxDimensions.width}px)`);
}
if (img.height > maxDimensions.height) {
errors.push(`Image too tall: ${img.height}px (max: ${maxDimensions.height}px)`);
}
resolve(errors);
};
img.src = URL.createObjectURL(file);
});
}
}
// Usage with Apisto
async function uploadWithValidation(file) {
const validationErrors = await FileValidator.validateFile(file, {
maxSize: 5 * 1024 * 1024, // 5MB
allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
allowedExtensions: ['jpg', 'jpeg', 'png', 'pdf'],
maxDimensions: { width: 4000, height: 4000 }
});
if (validationErrors.length > 0) {
throw new Error(`Validation failed: ${validationErrors.join(', ')}`);
}
// Use Apisto's built-in validation
return await api.validateAndUpload('/files/secure', file, {
maxSize: 5 * 1024 * 1024,
allowedTypes: ['image/jpeg', 'image/png', 'application/pdf']
});
}
// Secure file upload service
class SecureFileService {
constructor(api) {
this.api = api;
}
async uploadUserFile(file, userId, purpose) {
// Generate secure filename
const secureName = this.generateSecureFilename(file, userId);
// Upload with security headers
const result = await this.api.postFile('/files/secure', file, {
secureName,
userId,
purpose,
uploadTime: new Date().toISOString(),
ip: await this.getClientIP() // From your backend
}, {
headers: {
'X-File-Checksum': await this.calculateChecksum(file),
'X-Upload-Scope': 'user_upload'
}
});
// Log the upload for security auditing
await this.logUpload({
fileId: result.fileId,
userId,
originalName: file.name,
secureName,
size: file.size,
type: file.type,
purpose
});
return result;
}
generateSecureFilename(file, userId) {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 15);
const extension = file.name.split('.').pop();
return `user_${userId}_${timestamp}_${random}.${extension}`;
}
async calculateChecksum(file) {
// Simple checksum implementation
const buffer = await file.arrayBuffer();
const hash = new Uint8Array(buffer).reduce((a, b) => a + b, 0);
return hash.toString(16);
}
async getClientIP() {
// This would be set by your backend
return 'unknown';
}
async logUpload(details) {
// Send to your logging service
console.log('File upload logged:', details);
}
}
// Usage
const fileService = new SecureFileService(api);
const uploadResult = await fileService.uploadUserFile(
file,
'user123',
'profile_picture'
);
// Complete file management service
class FileManager {
constructor(api) {
this.api = api;
this.uploads = new Map();
this.downloads = new Map();
}
// UPLOAD MANAGEMENT
async uploadFile(file, options = {}) {
const uploadId = `upload_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const upload = {
id: uploadId,
file,
options,
progress: 0,
status: 'pending',
startTime: Date.now()
};
this.uploads.set(uploadId, upload);
try {
const apistoUpload = await this.api.upload.upload('/files', file, {
folder: options.folder,
description: options.description,
tags: options.tags || []
}, {
onProgress: (progress) => {
upload.progress = progress.percentage;
upload.status = 'uploading';
upload.speed = progress.bytesPerSecond;
if (options.onProgress) {
options.onProgress({
...progress,
uploadId,
file: upload.file
});
}
}
});
// Store for control
upload.apistoUpload = apistoUpload;
const result = await apistoUpload.promise;
upload.status = 'completed';
upload.result = result;
return result;
} catch (error) {
upload.status = 'error';
upload.error = error;
throw error;
}
}
// DOWNLOAD MANAGEMENT
async downloadFile(fileId, options = {}) {
const downloadId = `download_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const download = {
id: downloadId,
fileId,
options,
progress: 0,
status: 'pending',
startTime: Date.now()
};
this.downloads.set(downloadId, download);
try {
const fileInfo = await this.api.getFile(`/files/${fileId}`);
download.status = 'completed';
download.fileInfo = fileInfo;
return fileInfo;
} catch (error) {
download.status = 'error';
download.error = error;
throw error;
}
}
// BATCH OPERATIONS
async uploadMultiple(files, options = {}) {
const uploads = files.map(file =>
this.uploadFile(file, options)
);
return Promise.allSettled(uploads);
}
async downloadMultiple(fileIds, options = {}) {
const downloads = fileIds.map(fileId =>
this.downloadFile(fileId, options)
);
return Promise.allSettled(downloads);
}
// CONTROL METHODS
cancelUpload(uploadId) {
const upload = this.uploads.get(uploadId);
if (upload?.apistoUpload) {
upload.apistoUpload.cancel();
upload.status = 'cancelled';
}
}
pauseUpload(uploadId) {
const upload = this.uploads.get(uploadId);
if (upload?.apistoUpload) {
upload.apistoUpload.pause();
upload.status = 'paused';
}
}
resumeUpload(uploadId) {
const upload = this.uploads.get(uploadId);
if (upload?.apistoUpload) {
upload.apistoUpload.resume();
upload.status = 'uploading';
}
}
// STATUS METHODS
getUploadStatus(uploadId) {
return this.uploads.get(uploadId);
}
getDownloadStatus(downloadId) {
return this.downloads.get(downloadId);
}
getAllUploads() {
return Array.from(this.uploads.values());
}
getAllDownloads() {
return Array.from(this.downloads.values());
}
// CLEANUP
cleanupCompleted() {
for (const [id, upload] of this.uploads) {
if (upload.status === 'completed' || upload.status === 'error') {
this.uploads.delete(id);
}
}
for (const [id, download] of this.downloads) {
if (download.status === 'completed' || download.status === 'error') {
URL.revokeObjectURL(download.fileInfo?.url);
this.downloads.delete(id);
}
}
}
}
// Usage example
const fileManager = new FileManager(api);
// Upload a file with progress
const result = await fileManager.uploadFile(file, {
folder: 'documents',
description: 'Quarterly report',
onProgress: (progress) => {
updateUploadUI(progress);
}
});
// Download a file
const downloadedFile = await fileManager.downloadFile('file-123');
// Batch operations
const batchResults = await fileManager.uploadMultiple([file1, file2, file3], {
folder: 'photos',
onProgress: (progress) => {
console.log(`File ${progress.uploadId}: ${progress.percentage}%`);
}
});
🎉 Amazing! You've mastered comprehensive file handling with Apisto. You can now build sophisticated file upload/download systems with progress tracking, validation, and security.
Learn how to implement various authentication strategies including JWT, API keys, and OAuth2.
Continue to Authentication →