Search code examples
javascriptnode.jsreactjsmernipfs

Pinata IPFS's pinFileToIPFS method not accepting a user uploaded file


I am working on a project (using React.js Express.js and Node.js) to convert a user uploaded image into and NFT on Ethereum blockchain and for that, I need to upload the image on an IPFS (I am using Pinata) and then use the pinata URI in the metadata to mint a new NFT. (Do let me know if I am wrong here, I am still newbie to web3)

For uploading my image onto the Pinata IPFS, I am sending the base64 string of the image from the client side to the server side and then calling the pinFileToIPFS method. This is the code of my server side file

const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
const router = require('express').Router();
const { Readable } = require('stream');

const pinFileToIPFS = (image) => {
    const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`;
    const buffer = Buffer.from(image);
    const stream = Readable.from(buffer);
    const filename = `an-awesome-nft_${Date.now()}.png`;
    stream.path = filename;

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

return axios.post(url,
        formData,
        {
            headers: {
                'Content-Type': `multipart/form-data; boundary= ${formData._boundary}`,
                'pinata_api_key': "*******************",
                'pinata_secret_api_key': "**********************************",
            }
        }
    ).then(function (response) {
        console.log("Success: ", response);
    }).catch(function (error) {
        console.log("Fail! ", error.response.data);
    });
};

router.route('/').post((req, res) => {
    const image = req.body.image;
    pinFileToIPFS(image);
});

module.exports = router;

Here req.body.image contains the base64 string of the user uploaded file. I have tried to convert the base64 string into a buffer and then convert the buffer into a readable stream (as done in the official Pianata documentation but for a localy file) and then wrap it up in FormData(), but I keep getting the following error.

data: {
  error: 'This API endpoint requires valid JSON, and a JSON content-type'
}

I know the problem is with the format my image/file is being sent to the API but I can't figure out. I am still a newbie to web3 and blockchains so please help!


Solution

  • The recommended way of interacting with Pinata, is by using their Node.JS SDK. This SDK has a pinFileToIPFS function, allows you to upload an image to their IPFS nodes in the form of a readableStream. A sample of this would look like

    const fs = require('fs');
    const readableStreamForFile = fs.createReadStream('./yourfile.png');
    const options = {
        pinataMetadata: {
            name: MyCustomName,
            keyvalues: {
                customKey: 'customValue',
                customKey2: 'customValue2'
            }
        },
        pinataOptions: {
            cidVersion: 0
        }
    };
    pinata.pinFileToIPFS(readableStreamForFile, options).then((result) => {
        //handle results here
        console.log(result);
    }).catch((err) => {
        //handle error here
        console.log(err);
    });
    

    However, if you are deadset on using their API endpoints and simply posting to them via axios, there is a seperate API endpoint. /pinning/pinFileToIPFS. Examples of this method can be found in their API Docs.