Skip to main content

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 Media with Products

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.

Add Additional Product Media

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 Media with Categories

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 Media for Seller Applications

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"

Media Guidelines

Supported File Formats

Images:
  • JPEG (.jpg, .jpeg)
  • PNG (.png)
  • WebP (.webp)
  • SVG (.svg) - for icons only
Videos:
  • MP4 (.mp4)
  • WebM (.webm)
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

Image Optimization Tools

  • 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);

Build docs developers (and LLMs) love