Uploading Files in React

Loading

Handling file uploads in React applications requires special consideration compared to regular form data. Here’s a comprehensive guide to implementing file uploads effectively:

1. Basic File Upload

Using Native HTML Input

function BasicFileUpload() {
  const handleFileChange = (e) => {
    const file = e.target.files[0];
    console.log('Selected file:', file);
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
    </div>
  );
}

Handling Multiple Files

function MultiFileUpload() {
  const handleFilesChange = (e) => {
    const files = Array.from(e.target.files);
    console.log('Selected files:', files);
  };

  return (
    <div>
      <input 
        type="file" 
        multiple 
        onChange={handleFilesChange} 
      />
    </div>
  );
}

2. File Upload with Form Data

Using FormData API

function FormWithFileUpload() {
  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append('file', e.target.file.files[0]);
    formData.append('title', e.target.title.value);

    try {
      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData
      });
      const result = await response.json();
      console.log('Upload success:', result);
    } catch (error) {
      console.error('Upload failed:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="title" placeholder="File title" />
      <input type="file" name="file" />
      <button type="submit">Upload</button>
    </form>
  );
}

3. Previewing Files Before Upload

Image Preview

function ImageUploadWithPreview() {
  const [preview, setPreview] = useState(null);

  const handleImageChange = (e) => {
    const file = e.target.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onloadend = () => {
        setPreview(reader.result);
      };
      reader.readAsDataURL(file);
    }
  };

  return (
    <div>
      <input type="file" accept="image/*" onChange={handleImageChange} />
      {preview && (
        <div>
          <img src={preview} alt="Preview" style={{ maxWidth: '100%', maxHeight: '200px' }} />
        </div>
      )}
    </div>
  );
}

4. File Validation

Size and Type Validation

function ValidatedFileUpload() {
  const [error, setError] = useState('');

  const validateFile = (file) => {
    const validTypes = ['image/jpeg', 'image/png', 'image/gif'];
    if (!validTypes.includes(file.type)) {
      setError('Only JPG, PNG, or GIF files are allowed');
      return false;
    }
    if (file.size > 5 * 1024 * 1024) { // 5MB
      setError('File size must be less than 5MB');
      return false;
    }
    setError('');
    return true;
  };

  const handleFileChange = (e) => {
    const file = e.target.files[0];
    if (file && validateFile(file)) {
      console.log('Valid file selected:', file);
    }
  };

  return (
    <div>
      <input type="file" accept="image/*" onChange={handleFileChange} />
      {error && <div style={{ color: 'red' }}>{error}</div>}
    </div>
  );
}

5. Upload Progress Tracking

Tracking Upload Progress

function UploadWithProgress() {
  const [progress, setProgress] = useState(0);

  const handleUpload = async (e) => {
    const file = e.target.files[0];
    if (!file) return;

    const formData = new FormData();
    formData.append('file', file);

    const xhr = new XMLHttpRequest();

    xhr.upload.addEventListener('progress', (event) => {
      if (event.lengthComputable) {
        const percentComplete = Math.round((event.loaded / event.total) * 100);
        setProgress(percentComplete);
      }
    });

    xhr.open('POST', '/api/upload');
    xhr.send(formData);
  };

  return (
    <div>
      <input type="file" onChange={handleUpload} />
      {progress > 0 && (
        <div>
          <progress value={progress} max="100" />
          <span>{progress}%</span>
        </div>
      )}
    </div>
  );
}

6. Using Libraries for Advanced Features

React Dropzone (Drag & Drop)

import { useDropzone } from 'react-dropzone';

function DropzoneUpload() {
  const { acceptedFiles, getRootProps, getInputProps } = useDropzone({
    accept: {
      'image/*': ['.jpeg', '.png', '.gif']
    },
    maxFiles: 3,
    maxSize: 5 * 1024 * 1024 // 5MB
  });

  return (
    <div {...getRootProps({ className: 'dropzone' })}>
      <input {...getInputProps()} />
      <p>Drag & drop files here, or click to select</p>
      <ul>
        {acceptedFiles.map(file => (
          <li key={file.path}>
            {file.path} - {file.size} bytes
          </li>
        ))}
      </ul>
    </div>
  );
}

7. Handling Large Files (Chunk Uploads)

Chunked File Upload

async function uploadFileInChunks(file, chunkSize = 5 * 1024 * 1024) {
  const totalChunks = Math.ceil(file.size / chunkSize);
  const fileId = Date.now() + '-' + Math.random().toString(36).substr(2, 9);

  for (let chunkNumber = 0; chunkNumber < totalChunks; chunkNumber++) {
    const start = chunkNumber * chunkSize;
    const end = Math.min(start + chunkSize, file.size);
    const chunk = file.slice(start, end);

    const formData = new FormData();
    formData.append('fileId', fileId);
    formData.append('chunkNumber', chunkNumber);
    formData.append('totalChunks', totalChunks);
    formData.append('fileName', file.name);
    formData.append('chunk', chunk);

    await fetch('/api/upload-chunk', {
      method: 'POST',
      body: formData
    });
  }

  return fileId;
}

function ChunkedFileUpload() {
  const handleFileChange = async (e) => {
    const file = e.target.files[0];
    if (file) {
      const fileId = await uploadFileInChunks(file);
      console.log('Upload complete, file ID:', fileId);
    }
  };

  return <input type="file" onChange={handleFileChange} />;
}

Best Practices

  1. Client-Side Validation:
  • Validate file type (accept attribute)
  • Check file size before upload
  • Provide clear error messages
  1. Server-Side Validation:
  • Always validate on server (client validation can be bypassed)
  • Check MIME type, not just extension
  • Scan for malware if applicable
  1. User Experience:
  • Show previews for images
  • Display upload progress
  • Provide clear success/error feedback
  1. Security Considerations:
  • Use HTTPS for file uploads
  • Store files outside web root when possible
  • Implement proper file naming to prevent directory traversal
  1. Performance:
  • Consider chunked uploads for large files
  • Compress images before upload when appropriate
  • Implement client-side resizing for images

These techniques cover most file upload scenarios in React applications. Choose the approach that best fits your specific requirements and user experience goals.

Leave a Reply

Your email address will not be published. Required fields are marked *