Introduction to File Uploads

File uploads allow users to select and send files from their device to a web server. The <input type="file"> element provides the interface for file selection, while proper form configuration ensures successful file transmission.

Basic File Upload Setup

For file uploads to work properly, you need specific form attributes:



html

<form action="/upload" method="POST" enctype="multipart/form-data">
  <label for="file-upload">Choose file:</label>
  <input type="file" id="file-upload" name="file-upload">
  <button type="submit">Upload</button>
</form>

Key Requirements:

  • method="POST" - Files cannot be uploaded with GET method
  • enctype="multipart/form-data" - Essential for binary file data transmission

File Input Attributes

Accepting Specific File Types



html

<!-- Accept only images -->
<input type="file" name="image" accept="image/*">

<!-- Accept specific image formats -->
<input type="file" name="photo" accept="image/png, image/jpeg, image/gif">

<!-- Accept documents -->
<input type="file" name="document" accept=".pdf,.doc,.docx">

<!-- Accept audio files -->
<input type="file" name="audio" accept="audio/*">

<!-- Accept video files -->
<input type="file" name="video" accept="video/mp4, video/webm">

<!-- Mixed file types -->
<input type="file" name="media" accept="image/*, video/*, .pdf">

Multiple File Selection



html

<!-- Allow multiple file selection -->
<input type="file" name="files" multiple>

<!-- Multiple images -->
<input type="file" name="photos" accept="image/*" multiple>

Capture from Device Camera (Mobile)



html

<!-- Camera for photos -->
<input type="file" name="photo" accept="image/*" capture="environment">

<!-- Front camera for selfies -->
<input type="file" name="selfie" accept="image/*" capture="user">

<!-- Video recording -->
<input type="file" name="video" accept="video/*" capture="environment">

Complete File Upload Examples

Basic Image Upload with Preview



html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Image Upload with Preview</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 600px;
      margin: 20px auto;
      padding: 20px;
    }
    
    .upload-container {
      border: 2px dashed #ccc;
      border-radius: 8px;
      padding: 20px;
      text-align: center;
      margin-bottom: 20px;
      transition: border-color 0.3s;
    }
    
    .upload-container:hover {
      border-color: #007bff;
    }
    
    .upload-container.dragover {
      border-color: #007bff;
      background-color: #f8f9fa;
    }
    
    .file-input {
      display: none;
    }
    
    .upload-label {
      cursor: pointer;
      color: #007bff;
      font-weight: bold;
    }
    
    .preview-container {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-top: 20px;
    }
    
    .preview-item {
      position: relative;
      border: 1px solid #ddd;
      border-radius: 4px;
      overflow: hidden;
    }
    
    .preview-image {
      width: 150px;
      height: 150px;
      object-fit: cover;
    }
    
    .remove-btn {
      position: absolute;
      top: 5px;
      right: 5px;
      background-color: #dc3545;
      color: white;
      border: none;
      border-radius: 50%;
      width: 25px;
      height: 25px;
      cursor: pointer;
      font-size: 12px;
    }
    
    .file-info {
      padding: 10px;
      background-color: #f8f9fa;
      border-radius: 4px;
      margin-bottom: 10px;
    }
    
    .error {
      color: #dc3545;
      margin-top: 10px;
    }
    
    button {
      background-color: #007bff;
      color: white;
      padding: 10px 20px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 16px;
    }
    
    button:hover {
      background-color: #0056b3;
    }
    
    button:disabled {
      background-color: #6c757d;
      cursor: not-allowed;
    }
  </style>
</head>
<body>
  <h1>File Upload with Preview</h1>
  
  <form id="upload-form" enctype="multipart/form-data">
    <div class="upload-container" id="upload-container">
      <label for="file-input" class="upload-label">
        📁 Click to select files or drag and drop here
      </label>
      <input type="file" 
             id="file-input" 
             class="file-input" 
             name="files" 
             accept="image/*" 
             multiple>
      <p>Supported formats: JPG, PNG, GIF (max 5MB each)</p>
    </div>
    
    <div class="error" id="error-message"></div>
    
    <div class="preview-container" id="preview-container"></div>
    
    <button type="submit" id="upload-btn" disabled>Upload Files</button>
  </form>
  
  <script>
    const fileInput = document.getElementById('file-input');
    const uploadContainer = document.getElementById('upload-container');
    const previewContainer = document.getElementById('preview-container');
    const errorMessage = document.getElementById('error-message');
    const uploadBtn = document.getElementById('upload-btn');
    const form = document.getElementById('upload-form');
    
    let selectedFiles = [];
    const maxFileSize = 5 * 1024 * 1024; // 5MB
    const maxFiles = 10;
    
    // File selection handler
    fileInput.addEventListener('change', handleFiles);
    
    // Drag and drop handlers
    uploadContainer.addEventListener('dragover', (e) => {
      e.preventDefault();
      uploadContainer.classList.add('dragover');
    });
    
    uploadContainer.addEventListener('dragleave', () => {
      uploadContainer.classList.remove('dragover');
    });
    
    uploadContainer.addEventListener('drop', (e) => {
      e.preventDefault();
      uploadContainer.classList.remove('dragover');
      const files = Array.from(e.dataTransfer.files);
      processFiles(files);
    });
    
    function handleFiles(e) {
      const files = Array.from(e.target.files);
      processFiles(files);
    }
    
    function processFiles(files) {
      errorMessage.textContent = '';
      
      // Validate total number of files
      if (selectedFiles.length + files.length > maxFiles) {
        showError(`Maximum ${maxFiles} files allowed`);
        return;
      }
      
      files.forEach(file => {
        // Validate file type
        if (!file.type.startsWith('image/')) {
          showError(`${file.name} is not an image file`);
          return;
        }
        
        // Validate file size
        if (file.size > maxFileSize) {
          showError(`${file.name} exceeds 5MB limit`);
          return;
        }
        
        // Check for duplicates
        if (selectedFiles.some(f => f.name === file.name && f.size === file.size)) {
          showError(`${file.name} is already selected`);
          return;
        }
        
        selectedFiles.push(file);
        createPreview(file, selectedFiles.length - 1);
      });
      
      updateUploadButton();
    }
    
    function createPreview(file, index) {
      const previewItem = document.createElement('div');
      previewItem.className = 'preview-item';
      previewItem.dataset.index = index;
      
      if (file.type.startsWith('image/')) {
        const reader = new FileReader();
        reader.onload = (e) => {
          previewItem.innerHTML = `
            <img src="${e.target.result}" alt="${file.name}" class="preview-image">
            <button type="button" class="remove-btn" onclick="removeFile(${index})">&times;</button>
            <div class="file-info">
              <strong>${file.name}</strong><br>
              <small>${formatFileSize(file.size)}</small>
            </div>
          `;
        };
        reader.readAsDataURL(file);
      }
      
      previewContainer.appendChild(previewItem);
    }
    
    function removeFile(index) {
      selectedFiles.splice(index, 1);
      updatePreviews();
      updateUploadButton();
      errorMessage.textContent = '';
    }
    
    function updatePreviews() {
      previewContainer.innerHTML = '';
      selectedFiles.forEach((file, index) => {
        createPreview(file, index);
      });
    }
    
    function updateUploadButton() {
      uploadBtn.disabled = selectedFiles.length === 0;
      uploadBtn.textContent = selectedFiles.length > 0 
        ? `Upload ${selectedFiles.length} file(s)` 
        : 'Upload Files';
    }
    
    function showError(message) {
      errorMessage.textContent = message;
    }
    
    function formatFileSize(bytes) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const sizes = ['Bytes', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
    
    // Form submission
    form.addEventListener('submit', function(e) {
      e.preventDefault();
      
      if (selectedFiles.length === 0) {
        showError('Please select at least one file');
        return;
      }
      
      // Create FormData with selected files
      const formData = new FormData();
      selectedFiles.forEach((file, index) => {
        formData.append('files', file);
      });
      
      // Simulate upload (replace with actual upload logic)
      uploadBtn.disabled = true;
      uploadBtn.textContent = 'Uploading...';
      
      setTimeout(() => {
        alert('Files uploaded successfully!');
        selectedFiles = [];
        previewContainer.innerHTML = '';
        fileInput.value = '';
        updateUploadButton();
      }, 2000);
    });
  </script>
</body>
</html>

Document Upload with Progress



html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document Upload with Progress</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 500px;
      margin: 20px auto;
      padding: 20px;
    }
    
    .form-group {
      margin-bottom: 20px;
    }
    
    label {
      display: block;
      margin-bottom: 5px;
      font-weight: bold;
    }
    
    input[type="file"] {
      width: 100%;
      padding: 10px;
      border: 2px solid #ddd;
      border-radius: 4px;
      background-color: #f9f9f9;
    }
    
    .file-list {
      margin-top: 15px;
    }
    
    .file-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 4px;
      margin-bottom: 10px;
      background-color: #f8f9fa;
    }
    
    .file-details {
      flex: 1;
    }
    
    .file-name {
      font-weight: bold;
      margin-bottom: 2px;
    }
    
    .file-size {
      color: #666;
      font-size: 0.9em;
    }
    
    .progress-container {
      width: 100%;
      background-color: #e9ecef;
      border-radius: 4px;
      margin-top: 5px;
      height: 20px;
      overflow: hidden;
    }
    
    .progress-bar {
      height: 100%;
      background-color: #007bff;
      width: 0%;
      transition: width 0.3s;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;
      font-size: 0.8em;
    }
    
    .remove-file {
      background-color: #dc3545;
      color: white;
      border: none;
      border-radius: 4px;
      padding: 5px 10px;
      cursor: pointer;
      margin-left: 10px;
    }
    
    button {
      background-color: #007bff;
      color: white;
      padding: 12px 24px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 16px;
      width: 100%;
    }
    
    button:hover {
      background-color: #0056b3;
    }
    
    button:disabled {
      background-color: #6c757d;
      cursor: not-allowed;
    }
    
    .error {
      color: #dc3545;
      font-size: 0.9em;
      margin-top: 5px;
    }
    
    .success {
      color: #28a745;
      font-size: 0.9em;
      margin-top: 5px;
    }
  </style>
</head>
<body>
  <h1>Document Upload</h1>
  
  <form id="document-form">
    <div class="form-group">
      <label for="document-input">Select Documents:</label>
      <input type="file" 
             id="document-input" 
             name="documents" 
             accept=".pdf,.doc,.docx,.txt,.rtf" 
             multiple>
      <small>Accepted formats: PDF, DOC, DOCX, TXT, RTF (max 10MB each)</small>
    </div>
    
    <div class="file-list" id="file-list"></div>
    
    <button type="submit" id="upload-btn" disabled>Upload Documents</button>
  </form>
  
  <script>
    const documentInput = document.getElementById('document-input');
    const fileList = document.getElementById('file-list');
    const uploadBtn = document.getElementById('upload-btn');
    const form = document.getElementById('document-form');
    
    let selectedFiles = [];
    const maxFileSize = 10 * 1024 * 1024; // 10MB
    const allowedTypes = ['.pdf', '.doc', '.docx', '.txt', '.rtf'];
    
    documentInput.addEventListener('change', handleFileSelection);
    
    function handleFileSelection(e) {
      const files = Array.from(e.target.files);
      processDocumentFiles(files);
    }
    
    function processDocumentFiles(files) {
      files.forEach(file => {
        // Validate file type
        const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
        if (!allowedTypes.includes(fileExtension)) {
          showError(`${file.name} is not a supported document format`);
          return;
        }
        
        // Validate file size
        if (file.size > maxFileSize) {
          showError(`${file.name} exceeds 10MB limit`);
          return;
        }
        
        // Check for duplicates
        if (selectedFiles.some(f => f.name === file.name && f.size === file.size)) {
          showError(`${file.name} is already selected`);
          return;
        }
        
        const fileObj = {
          file: file,
          id: Date.now() + Math.random(),
          progress: 0,
          uploaded: false
        };
        
        selectedFiles.push(fileObj);
        addFileToList(fileObj);
      });
      
      updateUploadButton();
    }
    
    function addFileToList(fileObj) {
      const fileItem = document.createElement('div');
      fileItem.className = 'file-item';
      fileItem.dataset.id = fileObj.id;
      
      fileItem.innerHTML = `
        <div class="file-details">
          <div class="file-name">${fileObj.file.name}</div>
          <div class="file-size">${formatFileSize(fileObj.file.size)}</div>
          <div class="progress-container" style="display: none;">
            <div class="progress-bar">0%</div>
          </div>
        </div>
        <button type="button" class="remove-file" onclick="removeFile('${fileObj.id}')">Remove</button>
      `;
      
      fileList.appendChild(fileItem);
    }
    
    function removeFile(id) {
      selectedFiles = selectedFiles.filter(f => f.id != id);
      const fileItem = document.querySelector(`[data-id="${id}"]`);
      if (fileItem) {
        fileItem.remove();
      }
      updateUploadButton();
    }
    
    function updateUploadButton() {
      uploadBtn.disabled = selectedFiles.length === 0;
      uploadBtn.textContent = selectedFiles.length > 0 
        ? `Upload ${selectedFiles.length} document(s)` 
        : 'Upload Documents';
    }
    
    function updateProgress(id, progress) {
      const fileItem = document.querySelector(`[data-id="${id}"]`);
      if (fileItem) {
        const progressContainer = fileItem.querySelector('.progress-container');
        const progressBar = fileItem.querySelector('.progress-bar');
        
        progressContainer.style.display = 'block';
        progressBar.style.width = `${progress}%`;
        progressBar.textContent = `${progress}%`;
        
        if (progress === 100) {
          progressBar.style.backgroundColor = '#28a745';
          progressBar.textContent = 'Complete';
        }
      }
    }
    
    function simulateUpload(fileObj) {
      return new Promise((resolve) => {
        let progress = 0;
        const interval = setInterval(() => {
          progress += Math.random() * 20;
          if (progress >= 100) {
            progress = 100;
            clearInterval(interval);
            fileObj.uploaded = true;
            updateProgress(fileObj.id, progress);
            resolve();
          } else {
            updateProgress(fileObj.id, Math.floor(progress));
          }
        }, 200);
      });
    }
    
    function showError(message) {
      const errorDiv = document.createElement('div');
      errorDiv.className = 'error';
      errorDiv.textContent = message;
      form.appendChild(errorDiv);
      setTimeout(() => errorDiv.remove(), 5000);
    }
    
    function showSuccess(message) {
      const successDiv = document.createElement('div');
      successDiv.className = 'success';
      successDiv.textContent = message;
      form.appendChild(successDiv);
      setTimeout(() => successDiv.remove(), 5000);
    }
    
    function formatFileSize(bytes) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const sizes = ['Bytes', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
    
    // Form submission with progress
    form.addEventListener('submit', async function(e) {
      e.preventDefault();
      
      if (selectedFiles.length === 0) {
        showError('Please select at least one document');
        return;
      }
      
      uploadBtn.disabled = true;
      uploadBtn.textContent = 'Uploading...';
      
      try {
        // Upload files sequentially with progress
        for (const fileObj of selectedFiles) {
          if (!fileObj.uploaded) {
            await simulateUpload(fileObj);
          }
        }
        
        showSuccess('All documents uploaded successfully!');
        
        // Reset form after successful upload
        setTimeout(() => {
          selectedFiles = [];
          fileList.innerHTML = '';
          documentInput.value = '';
          updateUploadButton();
        }, 2000);
        
      } catch (error) {
        showError('Upload failed. Please try again.');
        uploadBtn.disabled = false;
        uploadBtn.textContent = 'Retry Upload';
      }
    });
  </script>
</body>
</html>

File Upload Security Best Practices

Client-Side Validation



html

<script>
  function validateFile(file) {
    const maxSize = 5 * 1024 * 1024; // 5MB
    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
    const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif'];
    
    // Check file size
    if (file.size > maxSize) {
      return { valid: false, message: 'File size exceeds 5MB limit' };
    }
    
    // Check MIME type
    if (!allowedTypes.includes(file.type)) {
      return { valid: false, message: 'Invalid file type' };
    }
    
    // Check file extension
    const extension = '.' + file.name.split('.').pop().toLowerCase();
    if (!allowedExtensions.includes(extension)) {
      return { valid: false, message: 'Invalid file extension' };
    }
    
    // Additional checks
    if (file.name.length > 255) {
      return { valid: false, message: 'Filename too long' };
    }
    
    return { valid: true, message: 'File is valid' };
  }
</script>

File Upload with Fetch API



html

<script>
  async function uploadFile(file) {
    const formData = new FormData();
    formData.append('file', file);
    formData.append('description', 'User uploaded file');
    
    try {
      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData,
        headers: {
          'X-Requested-With': 'XMLHttpRequest'
        }
      });
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const result = await response.json();
      return result;
      
    } catch (error) {
      console.error('Upload failed:', error);
      throw error;
    }
  }
  
  // Usage with progress tracking
  function uploadWithProgress(file, progressCallback) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      const formData = new FormData();
      formData.append('file', file);
      
      xhr.upload.addEventListener('progress', (e) => {
        if (e.lengthComputable) {
          const percentComplete = (e.loaded / e.total) * 100;
          progressCallback(Math.round(percentComplete));
        }
      });
      
      xhr.addEventListener('load', () => {
        if (xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText));
        } else {
          reject(new Error(`Upload failed with status ${xhr.status}`));
        }
      });
      
      xhr.addEventListener('error', () => {
        reject(new Error('Upload failed'));
      });
      
      xhr.open('POST', '/api/upload');
      xhr.send(formData);
    });
  }
</script>

Common File Upload Patterns

Profile Picture Upload



html

<div class="profile-upload">
  <div class="current-avatar">
    <img src="/default-avatar.png" alt="Current profile picture" id="avatar-preview">
  </div>
  <input type="file" id="avatar-input" name="avatar" accept="image/*" style="display: none;">
  <label for="avatar-input" class="upload-btn">Change Photo</label>
</div>

<script>
  document.getElementById('avatar-input').addEventListener('change', function(e) {
    const file = e.target.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = function(e) {
        document.getElementById('avatar-preview').src = e.target.result;
      };
      reader.readAsDataURL(file);
    }
  });
</script>

Bulk File Upload



html

<div class="bulk-upload">
  <input type="file" id="bulk-input" multiple accept=".csv,.xlsx,.json">
  <div class="upload-summary" id="upload-summary"></div>
</div>

<script>
  document.getElementById('bulk-input').addEventListener('change', function(e) {
    const files = Array.from(e.target.files);
    const summary = document.getElementById('upload-summary');
    
    summary.innerHTML = `
      <p>Selected ${files.length} files:</p>
      <ul>
        ${files.map(file => `<li>${file.name} (${formatFileSize(file.size)})</li>`).join('')}
      </ul>
      <p>Total size: ${formatFileSize(files.reduce((sum, file) => sum + file.size, 0))}</p>
    `;
  });
</script>

Important Security Considerations

  1. Always validate on server-side - Client-side validation can be bypassed
  2. Check file extensions AND MIME types - Both can be spoofed but provide layers of security
  3. Limit file sizes - Prevent server overload and storage issues
  4. Sanitize filenames - Remove dangerous characters and limit length
  5. Store uploads outside web root - Prevent direct execution of uploaded files
  6. Scan for malware - Use antivirus scanning for uploaded files
  7. Implement rate limiting - Prevent abuse and DoS attacks