When building an application (frontend or backend) that requires file management, developers typically face two choices: build a custom feature or purchase an enterprise management service.
Building a complete file management system or Content Management System (CMS) can be a significant time investment, especially when several robust, premium enterprise solutions already exist. However, while paying for an existing enterprise solution seems easier, the pricing tiers often become a major consideration.
This tutorial guide will offer developers an alternative by walking through how to quickly integrate a self-hosted file management application with a ReactJS application.
To follow along with this tutorial, you should have:
- Docker Engine installed
- Working knowledge of JavaScr…
When building an application (frontend or backend) that requires file management, developers typically face two choices: build a custom feature or purchase an enterprise management service.
Building a complete file management system or Content Management System (CMS) can be a significant time investment, especially when several robust, premium enterprise solutions already exist. However, while paying for an existing enterprise solution seems easier, the pricing tiers often become a major consideration.
This tutorial guide will offer developers an alternative by walking through how to quickly integrate a self-hosted file management application with a ReactJS application.
To follow along with this tutorial, you should have:
- Docker Engine installed
- Working knowledge of JavaScript and ReactJS
- General web development experience
Download Self-Hosted File Server (Free)
Here are the steps for setting up your self-hosted, Docker-based file server using the free BoltonSea Community Service.
Please follow these steps to deploy the service. You can also refer to the quick installer guide available at [Link to Guide] for additional details.
Pull/download the BoltonSea Community Service Image from DockerHub
docker pull boltonsea/cms_api:latest
Verify the Image Download
docker images boltonsea/cms_api
Run the container, exposing the service on port 9080 of your host machine (mapping to port 9200 on the container).
docker run -d --name myBoltonSeaCMS -p 9080:9200 boltonsea/cms_api
Our File Service now Live
The file service is available here: http://localhost:9080
Test an endpoint (via Chrome Browser): http://localhost:9080/organizations/1
Create the React Application
We will begin by creating the base React application using the following commands:
npx create-react-app my-app
cd my-app
Install Dependencies and Start the UI
npm install ajv --save-dev
npm start
The command terminal should display the following output, confirming successful compilation and providing the local access details:
Compiled successfully!
You can now view file-upload-app in the browser.
Local: http://localhost:3000
On Your Network: http://192.168.4.31:3000
Note that the development build is not optimized.
To create a production build, use npm run build.
webpack compiled successfully
Now, let’s test our react UI. It should look like this
Integrate with File Service API
To begin the UI integration process, please set up the necessary files:
- Navigate to the
my-app/srcfolder. - Create the file named
lib.jswithin that directory.
This is the template of the lib.js file.
// ---------------- DEFAULT CONSTANT DECLARATIONs ----------------
const organizationId = 1;
const mediaGroupId = 1;
// ------ Endpoint to our docker-based BoltonSea File Server ------
const FILE_SERVER_ENDPOINT = "http://localhost:9080";
const UPLOAD_ENDPOINT = `${FILE_SERVER_ENDPOINT}/media`;
const MEDIA_GROUP_ENDPOINT = `${FILE_SERVER_ENDPOINT}/mediagroup/${mediaGroupId}/content`;
// ----------------------------------
// function to upload files into the filer_server
export async function uploadFile(file) {
… …
}
// function to get files from the filer_server
export async function fetchData() {
… …
}
const mediaGroupContent = (content) => {
… …
}
We can see that there are three helper method function definitions here.
This method helps upload files to the file-server
function uploadFile(file)
This method helps fetch the list of files from the file-server
function fetchData()
This is a helper method to extract an API requests output
const mediaGroupContent = (content)
Copy the full content of the lib.js file.
// ---------------- DEFAULT CONSTANT DECLARATIONs ----------------
const organizationId = 1;
const mediaGroupId = 1;
// ---------------- Endpoint to our docker-based BoltonSea File Server ----------------
const FILE_SERVER_ENDPOINT = "http://localhost:9080";
const UPLOAD_ENDPOINT = `${FILE_SERVER_ENDPOINT}/media`;
const MEDIA_GROUP_ENDPOINT = `${FILE_SERVER_ENDPOINT}/mediagroup/${mediaGroupId}/content`;
// --------------------------------------------------------------------
export async function uploadFile(file) {
// Create an object of formData
const formData = new FormData();
// Update the formData object
formData.append("media_group_id", mediaGroupId);
formData.append("account_id", organizationId);
formData.append("file", file);
try {
const response = await fetch(UPLOAD_ENDPOINT, {
method: "POST",
body: formData,
});
if (response.ok) {
alert("File uploaded successfully!");
window.location.reload();
} else {
console.error("File upload failed:", response.statusText);
alert("File upload failed!");
}
} catch (error) {
console.error("Error during file upload:", error);
alert("An error occurred during file upload.");
}
}
export async function fetchData() {
let result = [];
try {
const response = await fetch(MEDIA_GROUP_ENDPOINT); // Replace with your API URL
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
result = await response.json();
result = mediaGroupContent(result.content);
} catch (error) {
console.error("Error Getting Response");
}
return result;
}
const mediaGroupContent = (content) => {
content = content.filter((item) => item != null);
return content.map((mediaItem) => {
let type = "document";
if (mediaItem?.mime_type?.includes("image")) {
type = "image";
}
mediaItem["type"] = type;
return mediaItem;
});
};
Please follow these steps to update the application files:
Navigate to App.js
- Update
App.js:
- Copy the contents.
- Navigate to the
App.jsfile and paste the copied contents into the file. - Refresh the user interface (UI).
Refresh the UI
import { useEffect, useState } from "react";
import { uploadFile, fetchData } from "./lib";
import logo from "./logo.svg";
import "./App.css";
let file_ = null;
function App() {
const [mediaList, setMediaList] = useState([]);
useEffect(() => {
fetchData()
.then(setMediaList)
.catch((e) => console.error("An error occured!", e));
}, []);
const handleChange = async (e) => {
file_ = e.target.files[0];
};
const uploadAction = async (e) => {
e.preventDefault();
if (file_) {
await uploadFile(file_);
file_ = null;
}
};
return (
<div className="App">
<div id="page_title">
<img src={logo} className="App-logo" alt="logo" height={100} />
<h1>Document Management Dashboard</h1>
</div>
<div id="upload-component" className="container">
<div className="input-group">
<input
type="file"
onChange={handleChange}
className="form-control"
id="fileInput"
aria-describedby="uploadButton"
aria-label="Upload"
/>
<button
className="btn btn-outline-primary"
type="button"
id="uploadButton"
onClick={uploadAction}
>
Upload
</button>
</div>
</div>
<div id="media_list" className="container text-center">
<div className="row">
{mediaList?.map((item, i) => (
<div key={i} className="col-12 col-sm-6 col-lg-4">
<div className="media_item">
{item.type === "image" && (
<img src={item.public_url} alt={item.original_file_name} />
)}
<div>
<a target="_blank" rel="noreferrer" href={item.public_url}>
{item.original_file_name}
</a>
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
}
export default App;
- Navigate to
App.css
- Copy the contents of the methods.
- Navigate to the
App.cssfile and paste the copied contents into the file. - Refresh the user interface (UI).
Refresh the UI
@import url("https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css");
body {
background-color: white;
font-family: "Roboto", sans-serif;
}
h1 {
color: #444;
text-align: center;
}
button {
background-color: #6161d4;
}
/* ------- Other Components ------- */
#page_title {
margin: 6rem;
text-align: center;
}
#upload-component {
margin: 3rem auto;
max-width: 50rem;
}
#media_list {
padding: 1rem 2rem;
background-color: #f8f8f8;
}
.media_item {
height: 15rem;
overflow: scroll;
margin: 1.5rem 1rem;
border-radius: 5px;
align-content: center;
background-color: #fff;
box-shadow: 0 0 5px #ccc; /* A black shadow, offset, blurred, and semi-transparent */
}
.media_item img {
max-height: 80%;
margin-bottom: 1em;
}
Live Test Our File-Service UI Dashboard
We need to conduct a test of our File Service using the Dashboard interface.
Please upload a few test files directly through the Dashboard UI and then verify that they can be successfully retrieved afterward. This will help us confirm that the upload and retrieval processes are functioning correctly.
Here is what my Dashboard looks like 👇 after my file uploads through the Dashboard.
Tools
- Docker File Server (BoltonSea - Community Plan)
- DashBoard (React APP)
Happy Coding!