The multi-file upload is a feature that allows users to perform a bulk file upload. It saves users the effort of picking each file one by one.
In a previous tutorial, we have seen how to create a React component for a single file upload. In this tutorial, we support multi-file upload by processing an array of files uploaded by users.
This example shows a progress bar to show the uploading progress. Since the multi-file upload takes a significant time, this progress bar will be helpful to know the status.
In this example, we use CDN and babel transpiler to build a React component with a JSX.
This React example includes the CDN path of React, ReactDOM, Axios and Babel libraries.
Note: The CDN based transpilation is not recommended for production. Rather, depend on any build tool like Webpack.
In the previous article, we use Vite
tool which performs babel tranpilation with respect to the babel.config.js
during the build for production.
The React component class do the following.
The React component created in this example is MultiFileUpload
. This is rendered into the HTML root created for deploying this React component via ReactDOM.
Initially, the UI shows the file input to upload multiple files. Once the chosen files are submitted, then the preview and progress bar HTML elements will be shown.
This condition is checked based on the useState of the counter that has the number of files posted on submit.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="form.css" />
<title>Multiple File Upload</title>
</head>
<body>
<h1>Multiple File Upload</h1>
<div id="multiple-file-upload-component"></div>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="multiple-file-upload.js"></script>
<script type="text/babel">
const { useState } = React;
const { render } = ReactDOM;
const MultiFileUpload = () => {
const [files, setFiles] = useState([]);
const [errorMessage, setErrorMessage] = useState("");
const [uploadProgress, setUploadProgress] = useState(0);
const [imagePreview, setImagePreview] = useState([]);
const [uploadButtonClicked, setUploadButtonClicked] = useState(false);
function clearState() {
setFiles([]);
setErrorMessage("");
setUploadProgress(0);
setImagePreview([]);
setUploadButtonClicked(false);
}
return (
<div>
{errorMessage && <p className="validation-message">{errorMessage}</p>}
<div className="file-upload-button">
<input
type="file"
multiple
accept="image/jpeg, image/png, image/gif"
onChange={handleFileOnChange}
/>
<button onClick={handleMultiFileUpload}>Upload</button>
</div>
{uploadButtonClicked && (
<div>
<div className="image-container">
{files.length > 0 && (
<div>
<h3>Preview :</h3>
</div>
)}
{files.map((file, index) => (
<div key={index} style={{ marginBottom: "10px" }}>
<div>
<img className="preview" src={imagePreview[index]} alt="Preview" />
</div>
</div>
))}
</div>
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${uploadProgress}%` }}
></div>
</div>
</div>
)}
</div>
);
};
ReactDOM.render(<MultiFileUpload />, document.getElementById("multiple-file-upload-component"));
</script>
</body>
</html>
This JavaScript contains functions for validating the uploaded file. After validation, it triggers an AJAX request to perform the server-side upload action if no error exists.
The validation is for making sure of the following conditions.
Once no error found and the upload request is posted, the multiple images preview are shown. The setImagePreviews()
method change the UI state with the recently uploaded image preview.
multiple-file-upload.js
function checkIsAllowedType(selectedFiles) {
const allowedTypes = ['.jpeg', '.png', '.gif'];
return selectedFiles.some(file => {
const fileExtension = file.name.split('.').pop().toLowerCase();
return allowedTypes.includes(`.${fileExtension}`);
});
}
async function handleMultipleFileUploadAjax(files, setUploadProgress, setErrorMessage) {
try {
const formData = new FormData();
files.forEach((file) => formData.append('file', file));
const response = await axios.post('upload-image.php', formData, {
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
},
});
console.log(response.data);
} catch (error) {
console.error(error);
}
}
const validateFile = (e, setFiles, setErrorMessage, setImagePreviews, clearState) => {
clearState();
const selectedFiles = Array.from(e.target.files);
const totalSize = selectedFiles.reduce((acc, file) => acc + file.size, 0);
if (totalSize > 2 * 1024 * 1024) {
setErrorMessage('Image size exceeds. It should be less than 2MB.');
} else {
const isAllowedFiles = checkIsAllowedType(selectedFiles);
if (!isAllowedFiles) {
setErrorMessage('Please select only image files (jpeg, png, gif).');
} else {
setFiles(selectedFiles);
const previews = selectedFiles.map((file) => URL.createObjectURL(file));
setImagePreviews(previews);
}
}
};
This code is as part of the MultiFileUpload
component definition. These two handlers calls settersĀ setImagePreview
and setUploadProgress
to show the graphical acknowledgement for the user action.
It updates the UI with the uploaded image thumbnails as preview. It also updates the percentage of upload completed in a sliding progress bar.
const handleFileOnChange = (e) => {
validateFile(
e,
setFiles,
setErrorMessage,
setImagePreview,
clearState
);
};
const handleMultiFileUpload = async () => {
if (files.length === 0) {
setErrorMessage("Please select atleast a file to upload.");
return;
}
setErrorMessage("");
setUploadProgress();
setUploadButtonClicked(true);
handleMultipleFileUploadAjax(files);
};
upload-image.php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
if (isset($_FILES['file'])) {
$uploadDirectory = __DIR__ . '/uploads/';
if (!file_exists($uploadDirectory)) {
mkdir($uploadDirectory, 0777, true);
}
$file = $_FILES['file'];
$fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
if (in_array($fileExtension, $allowedExtensions)) {
$newFilename = uniqid() . '.' . $fileExtension;
$uploadPath = $uploadDirectory . $newFilename;
if (move_uploaded_file($file['tmp_name'], $uploadPath)) {
echo json_encode(['message' => 'File uploaded successfully']);
} else {
echo json_encode(['error' => 'Failed to move the file']);
}
} else {
echo json_encode(['error' => 'File type not allowed']);
}
} else {
echo json_encode(['error' => 'No file found in the request']);
}
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
}
} else {
echo json_encode(['error' => 'Invalid request method']);
}
its a great example for beginners to learn the react concept by using your example.i have a request for you to upload more react concepts in upcoming days..
Thank you! Sure, I will post more concepts. Keep reading.