next.js 将文件上载到IPFS时出错:HTTPError:需要项目ID

hrirmatl  于 2022-12-18  发布在  其他
关注(0)|答案(4)|浏览(143)

目前正在制作一个用于发布聊天信息的基本DApp,类似于Twitter,只是构建在智能合约上。我正在使用hardhat并在localhost上运行我的应用程序。当创建个人资料时,我希望用户能够上传个人资料图片,但目前每当我尝试这样做时,我会收到以下错误:

POST https://ipfs.infura.io:5001/api/v0/add?stream-channels=true&progress=false 401 (Unauthorized)

伴随错误消息:

Error uploading file:  HTTPError: project id required

    at Object.errorHandler [as handleError] (core.js?edc8:103:1)
    at async Client.fetch (http.js?8f3e:149:1)
    at async addAll (add-all.js?93f2:36:1)
    at async last (index.js?7e49:13:1)
    at async Object.add (add.js?6672:22:1)

控制台显示此函数中发生错误:

const uploadToInfura = async (file) => {
      try {
        const added = await client.add({ content: file });
  
        const url = `https://ipfs.infura.io/ipfs/${added.path}`;
  
        setFileUrl(url);
      } catch (error) {
        console.log('Error uploading file: ', error);
      }
    };


我将附上下面这个页面的整个代码,如果你可以,请让我知道我需要修复,以便让这个错误停止发生。任何其他提示,我可以改善一般也将不胜感激:)

import { useState, useEffect, useContext, useCallback, useMemo } from 'react'
import { useRouter } from 'next/router';
import { useDropzone } from 'react-dropzone';
import Image from 'next/image';
import { useTheme } from 'next-themes';
import { ethers } from "ethers";
import Web3Modal from 'web3modal';

import { Input, Button, Banner, SearchBar, PostCard, PostCardNFT, SmallInput } from '../components';
import images from '../assets';
import DecentratwitterAbi from './contractsData/decentratwitter.json';
import DecentratwitterAddress from './contractsData/decentratwitter-address.json';
import { Home } from './index'

import { create as ipfsHttpClient } from 'ipfs-http-client';
const client = ipfsHttpClient('https://ipfs.infura.io:5001/api/v0');

const Profile = () => {
    const [profile, setProfile] = useState('');
    const [posts, setPosts] = useState('');
    const [nfts, setNfts] = useState('');
    const [fileUrl, setFileUrl] = useState(null);
    const [isloading, setIsLoading] = useState(true);
    const { theme } = useTheme();
    const [files] = useState([]);
    const [formInput, updateFormInput] = useState({ username: '' });
    const router = useRouter();
    
    
    const uploadToInfura = async (file) => {
      try {
        const added = await client.add({ content: file });
  
        const url = `https://ipfs.infura.io/ipfs/${added.path}`;
  
        setFileUrl(url);
      } catch (error) {
        console.log('Error uploading file: ', error);
      }
    };

    const createProfile = async () => {
      const web3Modal = new Web3Modal();
      const connection = await web3Modal.connect();
      const provider = new ethers.providers.Web3Provider(connection);
      const signer = provider.getSigner();
      const contract = new ethers.Contract(
        DecentratwitterAddress.address,
        DecentratwitterAbi.abi,
        signer
      );

      const { username } = formInput;
      if (!username || !fileUrl) return;
      /* first, upload to IPFS */
      const data = JSON.stringify({ username, avatar: fileUrl });
      try {
        const added = await client.add(data);
        const url = `https://ipfs.infura.io/ipfs/${added.path}`;
        /* after file is uploaded to IPFS, pass the URL to save it on Polygon */
        await contract.mint(url);
        fetchMyNFTs();
      } catch (error) {
        console.log('Error uploading file: ', error);
      }
    };

    const fetchProfile = async (nfts) => {
      const web3Modal = new Web3Modal();
      const connection = await web3Modal.connect();
      const provider = new ethers.providers.Web3Provider(connection);
      const signer = provider.getSigner();
      const contract = new ethers.Contract(
        DecentratwitterAddress.address,
        DecentratwitterAbi.abi,
        signer
      );

      const address = await contract.signer.getAddress();
      const id = await contract.profiles(address);
      const profile = nfts.find((i) => i.id.toString() === id.toString());
      setProfile(profile);
      setIsLoading(false);
    };

    const loadPosts = async () => {
      const web3Modal = new Web3Modal();
      const connection = await web3Modal.connect();
      const provider = new ethers.providers.Web3Provider(connection);
      const signer = provider.getSigner();
      const contract = new ethers.Contract(
        DecentratwitterAddress.address,
        DecentratwitterAbi.abi,
        signer
      );
        // Get user's address
        let address = await contract.signer.getAddress()
        setAddress(address)
        // Check if user owns an nft
        // and if they do set profile to true
        const balance = await contract.balanceOf(address)
        setHasProfile(() => balance > 0)
        // Get all posts
        let results = await contract.getAllPosts()
        // Fetch metadata of each post and add that to post object.
        let posts = await Promise.all(results.map(async i => {
            // use hash to fetch the post's metadata stored on ipfs 
            let response = await fetch(`https://ipfs.infura.io/ipfs/${i.hash}`)
            const metadataPost = await response.json()
            // get authors nft profile
            const nftId = await contract.profiles(i.author)
            // get uri url of nft profile
            const uri = await contract.tokenURI(nftId)
            // fetch nft profile metadata
            response = await fetch(uri)
            const metadataProfile = await response.json()
            // define author object
            const author = {
                address: i.author,
                username: metadataProfile.username,
                avatar: metadataProfile.avatar
            }
            // define post object
            let post = {
                id: i.id,
                content: metadataPost.post,
                tipAmount: i.tipAmount,
                author
            }
            return post
        }))
        posts = posts.sort((a, b) => b.tipAmount - a.tipAmount)
        // Sort posts from most tipped to least tipped. 
        setPosts(posts)
        setLoading(false)
    };

    const fetchMyNFTs = async () => {
      const web3Modal = new Web3Modal();
      const connection = await web3Modal.connect();
      const provider = new ethers.providers.Web3Provider(connection);
      const signer = provider.getSigner();
      const contract = new ethers.Contract(
        DecentratwitterAddress.address,
        DecentratwitterAbi.abi,
        signer
      );

      const results = await contract.getMyNfts();
      let nfts = await Promise.all(results.map(async i => {
        const uri = await contract.tokenURI(i);
        const response = await fetch(uri);
        const metadata = await response.json();
        return ({
          id: i,
          username: metadata.username,
          avatar: metadata.avatar
        });
      }));
      setNfts(nfts);
      fetchProfile(nfts);
    };

    const tip = async (post) => {

      const web3Modal = new Web3Modal();
      const connection = await web3Modal.connect();
      const provider = new ethers.providers.Web3Provider(connection);
      const signer = provider.getSigner();
      const contract = new ethers.Contract(
        DecentratwitterAddress.address,
        DecentratwitterAbi.abi,
        signer
      );
        // tip post owner
        await (await contract.tipPostOwner(post.id, { value: ethers.utils.parseEther(tipAmount) })).wait()
        fetchMyNFTs()
    }

  useEffect(() => {
    fetchMyNFTs()
      .then((nfts) => {
        setNfts(nfts);
        setIsLoading(false);
      });
  }, []);

  const onDrop = useCallback(async (acceptedFile) => {
    await uploadToInfura(acceptedFile[0]);
  }, []);

  const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject } = useDropzone({
    onDrop,
    accept: 'image/*',
    maxSize: 5000000,
  });

  const fileStyle = useMemo(
    () => (
      `dark:bg-nft-black-1 bg-white border dark:border-white border-nft-gray-2 flex flex-col items-center p-5 rounded-sm border-dashed  
       ${isDragActive ? ' border-file-active ' : ''} 
       ${isDragAccept ? ' border-file-accept ' : ''} 
       ${isDragReject ? ' border-file-reject ' : ''}`),
    [isDragActive, isDragReject, isDragAccept],
  );


  return (

    
    <div className="w-full flex justify-start items-center flex-col min-h-screen">
      <div className="w-full flexCenter flex-col">
      <Banner
          name={
            profile ? (
              <div>{profile.username}</div>
            ) : (
              "No profile, please create one"
            )
          }
          childStyles="text-center mb-4"
          parentStyles="h-80 justify-center"
        />

        <div className="flexCenter flex-col -mt-20 z-0">
          <div className="flexCenter w-40 h-40 sm:w-36 sm:h-36 p-1 bg-nft-black-2 rounded-full">
            {profile ? (
              <Image src={profile.avatar} className="rounded-full object-cover" objectFit="cover" width="200%" height="200%" alt='avatar' />
            ) : (
              "No Profile"
            )
            }
          </div>
          <p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-2xl mt-6"></p>
        </div>
      </div>
      

      
      {profile ? (
        <div>
        {nfts ? (
          nfts.map((nft, key) => {
          <div key={key} >
            <PostCardNFT 
              image={<Image src={nft.author.avatar} layout="fixed" width='60' height='40' alt='post' className='rounded-[6px]' />}
              content={nft.content}
              tipAmount={ethers.utils.formatEther(post.tipAmount)}
              address={shortenAddress(post.author.address)}
              tipInput={
                <div className='pb-2'>
                  <SmallInput 
                    inputType='number'
                    title='Tip'
                    placeholder='ETH'
                    handleClick={(e) => setTipAmount(e.target.value)}
                  />
                </div>
              }
              button={
                <Button 
                  btnName="Tip"
                  btnType="primary"
                  classStyles="rounded-xl"
                  handleClick={() => tip(post)}
                />
              }
            />
          </div>
          })
        ) : (
          <div className="text-2xl font-bold pt-20">
            No Posts, Create One ...
          </div>
        )}
        </div>
      ) : (
        <div className="w-full px-20">
          <Input
            inputType="input"
            title="Username"
            placeholder="Input Username"
            handleClick={(e) => updateFormInput({ ...formInput, username: e.target.value })} 
          />

        <div className="mt-16">
          <p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-xl">Profile Avatar</p>
          <div className="mt-4">
            <div {...getRootProps()} className={fileStyle}>
            <>
              {fileUrl ? (
                <div>
                  <Image
                    src={fileUrl}
                    className="object-cover"
                    objectFit="cover"
                    width="200%"
                    height="200%"
                    alt="Asset_file" />
                  </div>
              ) : (
                <div>
                  <input {...getInputProps()} /><div className="flexCenter flex-col text-center">
                    <p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-xl">JPG, PNG, GIF, SVG, WEBM, MP3, MP4. Max 100mb.</p>
                      <div className="my-12 w-full flex justify-center">
                        <Image
                          src={images.upload}
                          width={100}
                          height={100}
                          objectFit="contain"
                          alt="file upload"
                          className={theme === 'light' ? 'filter invert' : undefined}
                        />
                      </div>  
                    <p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-sm">Drag and Drop File</p>
                    <p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-sm mt-2">Or browse media on your device</p>
                  </div>
                </div>
              )}
            </>
            </div>
          </div>
        </div>
            
          <div className="mt-7 w-full flex justify-end">
            <Button
              btnName="Mint Profile"
              btnType="primary"
              classStyles="rounded-xl"
              handleClick={createProfile} />
          </div>
        </div>
      )}
    </div>
  );
};

export default Profile;
hc2pp10m

hc2pp10m1#

您缺少授权标头,该标头告诉Infura您是谁,以及您有权访问API。
以下是这些文档的片段:

const ipfsClient = require('ipfs-http-client');

const projectId = '1qmt...XXX';   // <---------- your Infura Project ID

const projectSecret = 'c920...XXX';  // <---------- your Infura Secret
// (for security concerns, consider saving these values in .env files)

const auth = 'Basic ' + Buffer.from(projectId + ':' + projectSecret).toString('base64');

const client = ipfsClient.create({
    host: 'ipfs.infura.io',
    port: 5001,
    protocol: 'https',
    headers: {
        authorization: auth,
    },
});

您只需将一个选项对象和凭据添加到

const client = ipfsHttpClient('https://ipfs.infura.io:5001/api/v0');

如上所示。
图片来源:https://docs.infura.io/infura/networks/ipfs/how-to/make-requests#ipfs-http-client

cu6pst1q

cu6pst1q2#

2022年8月这周我也遇到了这个问题。
授权证书是最近的要求。Infura在其网站上发表了2020-2021年的文章,其中没有提到任何授权证书。他们最近还发表了一篇文章和一段视频,其中确实要求通过API端点进行身份验证才能访问IPFS。下面是公共API的弃用链接,相关信息接近文章末尾。
https://blog.infura.io/post/introducing-ipfs-dedicated-gateways
将auth令牌存储在.env中通常被认为是最佳实践,这样在提交到公共git repo时就不会泄露它们。
不幸的是,如果你在线部署你的应用程序,这仍然不足以保护你的令牌。一旦你到达那里,有很多在线资源可以解决这个问题。

0g0grzrc

0g0grzrc3#

步骤1:通过输入卡详细信息创建IPFS Infura项目
步骤2:启用专用网关给予任何子域名.
步骤3:npm install --save buffer
步骤4:在项目中添加以下代码

import { Buffer } from 'buffer';
const ipfsClient = require('ipfs-http-client');

const projectId = '---Enter projectID from infura.io project---';
const projectSecret = '---Enter project secrate key from infura.io project---';
const auth =
'Basic ' + Buffer.from(projectId + ':' + projectSecret).toString('base64');

const client = ipfsClient.create({
  host: 'ipfs.infura.io',
  port: 5001,
  protocol: 'https',
  headers: {
    authorization: auth,
  },
});

步骤5:使用类似的专用网关来查看您上传的图像https://(your gateway name).infura-ipfs.io/ipfs/${result.path} https://github.com/dappuniversity/nft_marketplace/issues/5#issuecomment-1219131315

q7solyqu

q7solyqu4#

我浪费了一整天来解决这个问题,以不同的方式更新代码,最后我创建了新的应用程序在infura和测试代码与新的项目ID和秘密,它是为我工作:
脚本代码:

const projectId = process.env.IPFS_PROVIDER_PROJECTID;
const projectSecret = process.env.IPFS_PROVIDER_PROJECTSECRET;
const ipfsClient = require(‘ipfs-http-client’);

async function main() {

 const auth = 'Basic ’ + Buffer.from(projectId + ‘:’ + projectSecret).toString(‘base64’);
    const client = await ipfsClient.create({
    host: ‘ipfs.infura.io’,
    port: 5001,
    protocol: ‘https’,
    apiPath: ‘/api/v0’,
    headers: {
    authorization: auth
    }
})

try {

    const file = await client.add(‘test.png’)
    console.log(file)

} catch (error) {
   console.log(error)
}
}
main();

弯曲:
curl -X POST -F文件=@test.png -u“KKKKKKKKEY:SSSSSECRET”“https://ipfs.infura.io:5001/api/v0/add“--不安全-A“ curl ”
它似乎不工作与旧的细节!
谢谢

相关问题