Overview
Media upload in Wolfix.Server is integrated into product and category management endpoints. Media files are automatically handled when creating or updating products and categories.
Upload Main Product Photo
When creating a product, include the main photo in the multipart form request.
Example:
curl -X POST "https://your-server.com/api/products" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-F "name=Product Name" \
-F "description=Product description" \
-F "price=99.99" \
-F "quantity=50" \
-F "categoryId=category-uuid" \
-F "sellerId=seller-uuid" \
-F "mainPhoto=@/path/to/image.jpg"
See Create Product for full documentation.
Adds extra images or videos to an existing product.
Example:
curl -X PATCH "https://your-server.com/api/products/add-product-media" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-F "productId=product-uuid" \
-F "media=@/path/to/image1.jpg" \
-F "media=@/path/to/image2.jpg" \
-F "media=@/path/to/video.mp4"
See Seller Products - Add Media for details.
Upload Category Icon (Admin)
When creating a parent category, include an icon file.
Example:
curl -X POST "https://your-server.com/api/categories" \
-H "Authorization: Bearer YOUR_ADMIN_JWT_TOKEN" \
-F "name=Electronics" \
-F "icon=@/path/to/icon.svg"
Upload Category Image (Admin)
When creating a child category, include a category image.
Example:
curl -X POST "https://your-server.com/api/categories/parent-uuid" \
-H "Authorization: Bearer YOUR_ADMIN_JWT_TOKEN" \
-F "name=Smartphones" \
-F "image=@/path/to/category-image.jpg"
Upload Business Document
When applying to become a seller, upload required business documents.
Example:
curl -X POST "https://your-server.com/api/seller-applications/account-uuid" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-F "shopName=My Shop" \
-F "shopDescription=Description" \
-F "businessDocument=@/path/to/license.pdf" \
-F "phoneNumber=+1-555-123-4567"
Images:
- JPEG (.jpg, .jpeg)
- PNG (.png)
- WebP (.webp)
- SVG (.svg) - for icons only
Videos:
Documents:
- PDF (.pdf) - for business documents
File Size Limits
Maximum File Sizes:
- Product Images: 5MB per file
- Category Images: 2MB per file
- Category Icons: 500KB per file
- Product Videos: 50MB per file
- Business Documents: 5MB per file
Image Specifications
Product Images:
- Recommended Resolution: 1200x1200px or higher
- Aspect Ratio: 1:1 (square) preferred
- Color Mode: RGB
- Quality: High quality, well-lit photos
- Background: Clean, preferably white or neutral
Category Images:
- Recommended Resolution: 800x600px
- Aspect Ratio: 4:3 or 16:9
- Should be representative of the category
Category Icons:
- Format: SVG preferred, PNG acceptable
- Size: 64x64px to 128x128px
- Style: Simple, recognizable icons
Client-Side Upload Example
JavaScript File Upload
// Upload product with main photo
async function createProductWithPhoto(productData, photoFile) {
const formData = new FormData();
// Add product data
formData.append('name', productData.name);
formData.append('description', productData.description);
formData.append('price', productData.price);
formData.append('quantity', productData.quantity);
formData.append('categoryId', productData.categoryId);
formData.append('sellerId', productData.sellerId);
// Add photo file
formData.append('mainPhoto', photoFile);
// Add attributes as JSON
formData.append('attributes', JSON.stringify(productData.attributes));
const response = await fetch('https://your-server.com/api/products', {
method: 'POST',
headers: {
'Authorization': `Bearer ${jwtToken}`
},
body: formData
});
return response;
}
// Add multiple photos to existing product
async function addProductPhotos(productId, photoFiles) {
const formData = new FormData();
formData.append('productId', productId);
// Add multiple files
photoFiles.forEach(file => {
formData.append('media', file);
});
const response = await fetch('https://your-server.com/api/products/add-product-media', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${jwtToken}`
},
body: formData
});
return response;
}
// File input handler
function handleFileSelect(event) {
const files = event.target.files;
// Validate files
for (const file of files) {
// Check file size
if (file.size > 5 * 1024 * 1024) { // 5MB
alert(`${file.name} is too large. Maximum size is 5MB.`);
return;
}
// Check file type
if (!file.type.startsWith('image/')) {
alert(`${file.name} is not an image file.`);
return;
}
}
// Upload files
addProductPhotos(productId, Array.from(files));
}
React File Upload Component
import React, { useState } from 'react';
function ProductImageUpload({ productId, onUploadComplete }) {
const [uploading, setUploading] = useState(false);
const [preview, setPreview] = useState(null);
const handleFileChange = async (e) => {
const file = e.target.files[0];
if (!file) return;
// Validate file
if (file.size > 5 * 1024 * 1024) {
alert('File is too large. Maximum size is 5MB.');
return;
}
if (!file.type.startsWith('image/')) {
alert('Please select an image file.');
return;
}
// Show preview
const reader = new FileReader();
reader.onloadend = () => setPreview(reader.result);
reader.readAsDataURL(file);
// Upload
setUploading(true);
try {
const formData = new FormData();
formData.append('productId', productId);
formData.append('media', file);
const response = await fetch('/api/products/add-product-media', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: formData
});
if (response.ok) {
alert('Image uploaded successfully!');
onUploadComplete?.();
} else {
alert('Upload failed. Please try again.');
}
} catch (error) {
console.error('Upload error:', error);
alert('Upload failed. Please try again.');
} finally {
setUploading(false);
}
};
return (
<div>
<input
type="file"
accept="image/*"
onChange={handleFileChange}
disabled={uploading}
/>
{uploading && <p>Uploading...</p>}
{preview && <img src={preview} alt="Preview" style={{ maxWidth: '200px' }} />}
</div>
);
}
export default ProductImageUpload;
Best Practices
Optimize Images: Compress images before upload to reduce file size while maintaining quality
Use Descriptive Filenames: Name files descriptively (e.g., “red-headphones-front.jpg”)
Multiple Angles: Upload multiple photos showing product from different angles
Validate Client-Side: Always validate file types and sizes on the client before uploading
- TinyPNG - Compress PNG and JPEG images
- ImageOptim - Reduce image file sizes
- Sharp (Node.js) - Server-side image processing
- Pillow (Python) - Image manipulation library
Progressive Enhancement
// Show upload progress
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
updateProgressBar(percentComplete);
}
});
xhr.addEventListener('load', () => {
if (xhr.status === 204) {
showSuccess('Upload complete!');
} else {
showError('Upload failed.');
}
});
xhr.open('PATCH', '/api/products/add-product-media');
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
xhr.send(formData);