<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ #minio - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ #minio - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 17 May 2026 16:32:44 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/minio/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Dropbox-like Distributed File Storage System Using MinIO and gRPC ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, I’ll guide you through building a distributed file storage system inspired by Dropbox, using MinIO (an open-source, S3-compatible object storage server) and gRPC. The goal is to create a system that can store, replicate, and manage ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-dropbox-like-distributed-file-storage-system-using-minio-and-grpc/</link>
                <guid isPermaLink="false">6733b4dc73da063aa0407447</guid>
                
                    <category>
                        <![CDATA[ #minio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ gRPC ]]>
                    </category>
                
                    <category>
                        <![CDATA[ storage ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ minio object storage ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Tue, 12 Nov 2024 20:04:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/GWQ67jjUg9g/upload/e37080969188b807a15d6ebdaf813fa2.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, I’ll guide you through building a <strong>distributed file storage system</strong> inspired by Dropbox, using MinIO (an open-source, S3-compatible object storage server) and gRPC. The goal is to create a system that can <strong>store, replicate, and manage files</strong> across multiple nodes, ensuring data availability and resilience.</p>
<p>We'll implement core features like file replication, metadata management, and versioning, all while demonstrating how to achieve eventual consistency in a distributed environment. By the end, you'll have a fully functional distributed file storage system that can handle high traffic, optimize storage, and ensure data integrity.</p>
<h3 id="heading-what-you-will-learn">What You Will Learn</h3>
<ul>
<li><p>How to set up <strong>MinIO</strong> for distributed object storage.</p>
</li>
<li><p>How to use <strong>gRPC</strong> for efficient client-server communication.</p>
</li>
<li><p>How to implement <strong>file replication</strong> and <strong>metadata management</strong>.</p>
</li>
<li><p>How to understand <strong>data consistency</strong> in a distributed system.</p>
</li>
<li><p>How to use <strong>Docker</strong> to deploy a scalable, distributed architecture.</p>
</li>
</ul>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before starting, ensure you have the following installed:</p>
<ul>
<li><p>Node.js (v14 or higher)</p>
</li>
<li><p>MinIO</p>
</li>
<li><p>gRPC and gRPC-tools</p>
</li>
<li><p>Docker</p>
</li>
</ul>
<p>You’ll also need to have a basic understanding of Node.js, object storage, and distributed systems.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-overview">Project Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-setting-up-the-project">Step 1: Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-setting-up-minio-distributed-storage-nodes">Step 2: Setting Up MinIO Distributed Storage Nodes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-defining-the-grpc-protocol">Step 3: Defining the gRPC Protocol</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-implementing-the-grpc-server">Step 4: Implementing the gRPC Server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-creating-the-client">Step 5: Creating the Client</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-running-the-system">Step 6: Running the System</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</a></p>
</li>
</ul>
<h2 id="heading-project-overview">Project Overview</h2>
<p>We'll build a <strong>distributed file storage system</strong> where:</p>
<ol>
<li><p>Users can upload and download files.</p>
</li>
<li><p>Files are replicated across multiple storage nodes to ensure high availability.</p>
</li>
<li><p>Metadata (like file names, upload times, and versions) is managed centrally.</p>
</li>
<li><p>The system handles <strong>eventual consistency</strong> by syncing file updates across nodes.</p>
</li>
</ol>
<h3 id="heading-system-architecture">System Architecture</h3>
<p>Our system will consist of:</p>
<ol>
<li><p><strong>gRPC Server</strong>: Manages file uploads, downloads, and metadata.</p>
</li>
<li><p><strong>MinIO Distributed Storage Nodes</strong>: Handles object storage and replication.</p>
</li>
<li><p><strong>Client Interface</strong>: Allows users to interact with the system via HTTP.</p>
</li>
</ol>
<h2 id="heading-step-1-setting-up-the-project">Step 1: Setting Up the Project</h2>
<p>Create a new directory for the project and initialize a Node.js application:</p>
<pre><code class="lang-javascript">mkdir distributed-file-storage
cd distributed-file-storage
npm init -y
</code></pre>
<p>Now, install the necessary dependencies:</p>
<pre><code class="lang-javascript">npm install grpc @grpc/grpc-js @grpc/proto-loader express multer dotenv minio
</code></pre>
<ul>
<li><p><strong>grpc</strong>: For building gRPC server and client.</p>
</li>
<li><p><strong>@grpc/proto-loader</strong>: Loads gRPC protocol files.</p>
</li>
<li><p><strong>express</strong>: For the client-side HTTP server.</p>
</li>
<li><p><strong>multer</strong>: For handling file uploads.</p>
</li>
<li><p><strong>dotenv</strong>: For managing environment variables.</p>
</li>
<li><p><strong>minio</strong>: MinIO client for interacting with storage nodes.</p>
</li>
</ul>
<p>Create a <strong>.env</strong> file with the following content:</p>
<pre><code class="lang-javascript">MINIO_ENDPOINT_1=localhost:<span class="hljs-number">9001</span>
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
PORT=<span class="hljs-number">5000</span>
</code></pre>
<h2 id="heading-step-2-setting-up-minio-distributed-storage-nodes">Step 2: Setting Up MinIO Distributed Storage Nodes</h2>
<p>We'll use <strong>Docker</strong> to run multiple MinIO instances, simulating a distributed environment. Run the following commands to set up three MinIO containers:</p>
<pre><code class="lang-javascript">docker run -p <span class="hljs-number">9001</span>:<span class="hljs-number">9000</span> --name minio1 -e <span class="hljs-string">"MINIO_ACCESS_KEY=minioadmin"</span> -e <span class="hljs-string">"MINIO_SECRET_KEY=minioadmin"</span> -d minio/minio server /data
docker run -p <span class="hljs-number">9002</span>:<span class="hljs-number">9000</span> --name minio2 -e <span class="hljs-string">"MINIO_ACCESS_KEY=minioadmin"</span> -e <span class="hljs-string">"MINIO_SECRET_KEY=minioadmin"</span> -d minio/minio server /data
docker run -p <span class="hljs-number">9003</span>:<span class="hljs-number">9000</span> --name minio3 -e <span class="hljs-string">"MINIO_ACCESS_KEY=minioadmin"</span> -e <span class="hljs-string">"MINIO_SECRET_KEY=minioadmin"</span> -d minio/minio server /data
</code></pre>
<p>These commands will start three MinIO nodes, each listening on a different port.</p>
<h2 id="heading-step-3-defining-the-grpc-protocol">Step 3: Defining the gRPC Protocol</h2>
<p>Create a new folder named <strong>protos</strong> and inside it, create a file called <strong>storage.proto</strong>:</p>
<pre><code class="lang-javascript">syntax = <span class="hljs-string">"proto3"</span>;

service FileStorage {
  rpc UploadFile(stream FileRequest) returns (UploadResponse);
  rpc DownloadFile(FileDownloadRequest) returns (stream FileResponse);
  rpc GetMetadata(FileMetadataRequest) returns (MetadataResponse);
}

message FileRequest {
  bytes fileData = <span class="hljs-number">1</span>;
  string fileName = <span class="hljs-number">2</span>;
}

message UploadResponse {
  string message = <span class="hljs-number">1</span>;
}

message FileDownloadRequest {
  string fileName = <span class="hljs-number">1</span>;
}

message FileResponse {
  bytes fileData = <span class="hljs-number">1</span>;
}

message FileMetadataRequest {
  string fileName = <span class="hljs-number">1</span>;
}

message MetadataResponse {
  string fileName = <span class="hljs-number">1</span>;
  string uploadTime = <span class="hljs-number">2</span>;
  string version = <span class="hljs-number">3</span>;
}
</code></pre>
<ul>
<li><p><strong>UploadFile</strong>: Streams file data from the client to the server.</p>
</li>
<li><p><strong>DownloadFile</strong>: Streams file data from the server to the client.</p>
</li>
<li><p><strong>GetMetadata</strong>: Retrieves metadata like file name, upload time, and version.</p>
</li>
</ul>
<h2 id="heading-step-4-implementing-the-grpc-server">Step 4: Implementing the gRPC Server</h2>
<p>Create a file called <strong>server.js</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
<span class="hljs-keyword">const</span> grpc = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@grpc/grpc-js'</span>);
<span class="hljs-keyword">const</span> protoLoader = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@grpc/proto-loader'</span>);
<span class="hljs-keyword">const</span> Minio = <span class="hljs-built_in">require</span>(<span class="hljs-string">'minio'</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);

<span class="hljs-keyword">const</span> packageDefinition = protoLoader.loadSync(<span class="hljs-string">'protos/storage.proto'</span>);
<span class="hljs-keyword">const</span> storageProto = grpc.loadPackageDefinition(packageDefinition).FileStorage;

<span class="hljs-comment">// Set up MinIO clients for each node</span>
<span class="hljs-keyword">const</span> minioClients = [
  <span class="hljs-keyword">new</span> Minio.Client({
    <span class="hljs-attr">endPoint</span>: process.env.MINIO_ENDPOINT_1.split(<span class="hljs-string">':'</span>)[<span class="hljs-number">0</span>],
    <span class="hljs-attr">port</span>: <span class="hljs-built_in">parseInt</span>(process.env.MINIO_ENDPOINT_1.split(<span class="hljs-string">':'</span>)[<span class="hljs-number">1</span>]),
    <span class="hljs-attr">accessKey</span>: process.env.MINIO_ACCESS_KEY,
    <span class="hljs-attr">secretKey</span>: process.env.MINIO_SECRET_KEY,
    <span class="hljs-attr">useSSL</span>: <span class="hljs-literal">false</span>,
  })
];

<span class="hljs-comment">// Upload file to MinIO</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uploadFile</span>(<span class="hljs-params">call, callback</span>) </span>{
  <span class="hljs-keyword">const</span> chunks = [];
  call.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> chunks.push(chunk.fileData));
  call.on(<span class="hljs-string">'end'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> buffer = Buffer.concat(chunks);
    <span class="hljs-keyword">const</span> fileName = call.metadata.get(<span class="hljs-string">'fileName'</span>)[<span class="hljs-number">0</span>];

    <span class="hljs-comment">// Store file in MinIO</span>
    <span class="hljs-keyword">const</span> client = minioClients[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">await</span> client.putObject(<span class="hljs-string">'files'</span>, fileName, buffer);
    callback(<span class="hljs-literal">null</span>, { <span class="hljs-attr">message</span>: <span class="hljs-string">`File <span class="hljs-subst">${fileName}</span> uploaded successfully`</span> });
  });
}

<span class="hljs-comment">// Download file from MinIO</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">downloadFile</span>(<span class="hljs-params">call</span>) </span>{
  <span class="hljs-keyword">const</span> { fileName } = call.request;
  <span class="hljs-keyword">const</span> client = minioClients[<span class="hljs-number">0</span>];

  client.getObject(<span class="hljs-string">'files'</span>, fileName, <span class="hljs-function">(<span class="hljs-params">err, stream</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err) <span class="hljs-keyword">return</span> call.emit(<span class="hljs-string">'error'</span>, err);
    stream.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> call.write({ <span class="hljs-attr">fileData</span>: chunk }));
    stream.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> call.end());
  });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> server = <span class="hljs-keyword">new</span> grpc.Server();
  server.addService(storageProto.FileStorage.service, { uploadFile, downloadFile });
  server.bindAsync(<span class="hljs-string">'0.0.0.0:5000'</span>, grpc.ServerCredentials.createInsecure(), <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'gRPC server running on port 5000'</span>);
    server.start();
  });
}

main();
</code></pre>
<p>Here’s what’s going on in this code:</p>
<ol>
<li><p><strong>uploadFile</strong>: Handles file uploads by streaming data to the server and storing it in MinIO.</p>
</li>
<li><p><strong>downloadFile</strong>: Streams the requested file back to the client from MinIO.</p>
</li>
<li><p><strong>MinIO Clients</strong>: We set up multiple MinIO clients to handle distributed storage.</p>
</li>
</ol>
<h2 id="heading-step-5-creating-the-client">Step 5: Creating the Client</h2>
<p>Create a file named <strong>client.js</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> grpc = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@grpc/grpc-js'</span>);
<span class="hljs-keyword">const</span> protoLoader = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@grpc/proto-loader'</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);

<span class="hljs-keyword">const</span> packageDefinition = protoLoader.loadSync(<span class="hljs-string">'protos/storage.proto'</span>);
<span class="hljs-keyword">const</span> storageProto = grpc.loadPackageDefinition(packageDefinition).FileStorage;
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> storageProto(<span class="hljs-string">'localhost:5000'</span>, grpc.credentials.createInsecure());

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uploadFile</span>(<span class="hljs-params">filePath</span>) </span>{
  <span class="hljs-keyword">const</span> call = client.uploadFile();
  <span class="hljs-keyword">const</span> fileName = filePath.split(<span class="hljs-string">'/'</span>).pop();
  <span class="hljs-keyword">const</span> stream = fs.createReadStream(filePath);

  stream.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> call.write({ <span class="hljs-attr">fileData</span>: chunk }));
  stream.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> call.end());
  call.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(response.message));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">downloadFile</span>(<span class="hljs-params">fileName</span>) </span>{
  <span class="hljs-keyword">const</span> call = client.downloadFile({ fileName });
  <span class="hljs-keyword">const</span> writeStream = fs.createWriteStream(<span class="hljs-string">`downloaded_<span class="hljs-subst">${fileName}</span>`</span>);

  call.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> writeStream.write(chunk.fileData));
  call.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Downloaded <span class="hljs-subst">${fileName}</span>`</span>));
}

uploadFile(<span class="hljs-string">'test.txt'</span>);  <span class="hljs-comment">// Example usage</span>
</code></pre>
<h2 id="heading-step-6-running-the-system">Step 6: Running the System</h2>
<ol>
<li><p><strong>Start the gRPC Server</strong>:</p>
<pre><code class="lang-javascript"> node server.js
</code></pre>
</li>
<li><p><strong>Run the Client</strong>:</p>
<pre><code class="lang-javascript"> node client.js
</code></pre>
</li>
</ol>
<h2 id="heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</h2>
<p>Congratulations! You've built a distributed file storage system using <strong>MinIO</strong> and <strong>gRPC</strong>. In this tutorial, you learned how to:</p>
<ol>
<li><p>Set up a <strong>distributed object storage</strong> system using MinIO.</p>
</li>
<li><p>Use <strong>gRPC</strong> to handle file uploads, downloads, and metadata management.</p>
</li>
<li><p>Implement <strong>file replication</strong> and <strong>eventual consistency</strong> across multiple nodes.</p>
</li>
<li><p>Utilize <strong>Docker</strong> to simulate a scalable distributed environment.</p>
</li>
</ol>
<h3 id="heading-next-steps">Next Steps:</h3>
<ol>
<li><p><strong>Add File Versioning</strong>: Store multiple versions of files for rollback.</p>
</li>
<li><p><strong>Implement Authentication</strong>: Secure your gRPC endpoints with JWT.</p>
</li>
<li><p><strong>Deploy with Kubernetes</strong>: Scale your system across multiple nodes for high availability.</p>
</li>
</ol>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
