Building a Decentralized Image Sharing System with IPFS and Blockchain
S I D D A R T H

S I D D A R T H @kaniska

About: B for getting Blocked.

Joined:
Mar 23, 2025

Building a Decentralized Image Sharing System with IPFS and Blockchain

Publish Date: Mar 23
0 0

Introduction

Decentralization is the future of data storage and sharing. In this blog, we will explore how to build a decentralized image-sharing system using IPFS (InterPlanetary File System) for storage and Ethereum smart contracts for ownership tracking. We will also go through the mistakes made during the process and how they were fixed.

This Blog writing is only for own use (I written this for only me to remember what i did in free time)

Reach me at github @guider23

Get Files

What We Built

We created a smart contract that:

  • Allows users to upload an image to IPFS and store its hash on the blockchain.
  • Tracks image ownership by linking an Ethereum address to each image hash.
  • Provides a function to retrieve all stored images along with their owners.

By the end of this guide, you’ll understand how to integrate IPFS with Ethereum smart contracts and interact with them using Hardhat and Ethers.js.

1. Setting Up the Project

Install Dependencies

First, install Hardhat and the required packages:

mkdir blockchain-image-sharing
cd blockchain-image-sharing
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox ethers dotenv
Enter fullscreen mode Exit fullscreen mode

Initialize Hardhat

Run Hardhat setup:

npx hardhat
Enter fullscreen mode Exit fullscreen mode

Select “Create a basic sample project” and follow the prompts.

Install IPFS

We will use local IPFS for testing:

npm install ipfs-http-client
Enter fullscreen mode Exit fullscreen mode

Start a local IPFS node by running:

ipfs daemon
Enter fullscreen mode Exit fullscreen mode

2. Writing the Smart Contract

Create a new file contracts/ImageStorage.sol and add the following code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract ImageStorage {
    mapping(string => address) public imageOwners;
    string[] public imageHashes;

    event ImageUploaded(string ipfsHash, address owner);

    function uploadFile(string memory _hash) public {
        require(imageOwners[_hash] == address(0), "Image already uploaded!");
        imageOwners[_hash] = msg.sender;
        imageHashes.push(_hash);
        emit ImageUploaded(_hash, msg.sender);
    }

    function getImageOwner(string memory _hash) public view returns (address) {
        return imageOwners[_hash];
    }

    function getImages() public view returns (string[] memory) {
        return imageHashes;
    }
}
Enter fullscreen mode Exit fullscreen mode

This contract does the following:

  1. Stores IPFS hashes mapped to Ethereum addresses.
  2. Prevents duplicate uploads.
  3. Emits an event when an image is uploaded.
  4. Retrieves all stored images and owners.

3. Deploying the Smart Contract

Create a scripts/deploy.js file:

const hre = require("hardhat");
async function main() {
  const ImageStorage = await hre.ethers.getContractFactory("ImageStorage");
  const contract = await ImageStorage.deploy();

  await contract.waitForDeployment();
  console.log("Contract deployed to:", await contract.getAddress());
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
Enter fullscreen mode Exit fullscreen mode

Run the Hardhat local blockchain:

npx hardhat node
Enter fullscreen mode Exit fullscreen mode

Deploy the contract:

npx hardhat run scripts/deploy.js --network localhost
Enter fullscreen mode Exit fullscreen mode

You should see an output like:

Contract deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Enter fullscreen mode Exit fullscreen mode

4. Interacting with the Smart Contract

Open Hardhat Console

npx hardhat console --network localhost
Enter fullscreen mode Exit fullscreen mode

Connect to the Contract

const contract = await ethers.getContractAt("ImageStorage", "0x5FbDB2315678afecb367f032d93F642f64180aa3");
Enter fullscreen mode Exit fullscreen mode

Upload an Image Hash

await contract.uploadFile("QmfJwqRTWPp4Ag4j2tzcUWBcYZZ3JHoEdzpmfFyRx1aZ4a");
Enter fullscreen mode Exit fullscreen mode

Retrieve Uploaded Images

const images = await contract.getImages();
console.log(images);
Enter fullscreen mode Exit fullscreen mode

Expected Output:

['QmfJwqRTWPp4Ag4j2tzcUWBcYZZ3JHoEdzpmfFyRx1aZ4a']
Enter fullscreen mode Exit fullscreen mode

Get Image Owner

const owner = await contract.getImageOwner("QmfJwqRTWPp4Ag4j2tzcUWBcYZZ3JHoEdzpmfFyRx1aZ4a");
console.log("Owner Address:", owner);
Enter fullscreen mode Exit fullscreen mode

Expected Output:


Owner Address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

const imageList = await contract.getImages();
console.log(imageList);

//Get a array of stored hashes
Enter fullscreen mode Exit fullscreen mode

5. Common Mistakes and Fixes

1. Function Not Found Error

Issue:

Uncaught TypeError: contract.getFiles is not a function
Enter fullscreen mode Exit fullscreen mode

Fix: Ensure the function name matches exactly as defined in Solidity (getImages instead of getFiles).

2. “Contract Not Defined” Error

Issue:

Uncaught ReferenceError: contract is not defined
Enter fullscreen mode Exit fullscreen mode

Fix: Ensure you have correctly initialized the contract instance before calling functions.

3. Duplicate Upload Prevention

Issue: Uploading the same image hash again gives an error. Fix: This is intentional to prevent duplicate storage on the blockchain.

6. Next Steps

Now that we have a working decentralized image-sharing system, we can:

  • Build a Frontend using React.js to interact with the contract.
  • Implement User Authentication using MetaMask.
  • Deploy the Contract to a Public Testnet like Goerli or Sepolia.

Stay tuned for the next part where we integrate this with a frontend!


Comments 0 total

    Add comment