Next.js Polygon, Solidity,The Graph,IPFS,Hardhat web3博客系统

news2024/11/29 8:39:56

参考

源文档The Complete Guide to Full Stack Web3 Development - DEV Community

源码,源文章里的github项目无法直接运行,经过修改后可mac中可用GitHub - daocodedao/web3-blog: https://linzhji.blog.csdn.net/article/details/130125634

框架

博客系统将会部署在polygon,因为polygon交易费用比较低。整体项目框架

  • 区块链:Hardhat,polygon
  • eth开发环境:Hardhat
  • 前端框架:Next.js 和 React
  • 文件存储:IPFS
  • 检索: The Graph Protocol

 前置准备

  • node.js 环境
  • vscode
  • metamask钱包

开始开发

创建项目

 npx create-next-app web3-blog

cd web3-blog

 丰富一下package.json,新增

    "@openzeppelin/contracts": "^4.3.2",
    "@walletconnect/web3-provider": "^1.6.6",
    "hardhat": "^2.6.7",
    "ipfs-http-client": "^56.0.0",
    "web3modal": "^1.9.4",
    "react-markdown": "^7.1.0",
    "react-simplemde-editor": "^5.0.2",
    "@emotion/css": "^11.5.0"

  },
  "devDependencies": {
    "@nomiclabs/hardhat-ethers": "^2.0.0",
    "@nomiclabs/hardhat-waffle": "^2.0.0",
    "chai": "^4.2.0",
    "eslint": "7",
    "eslint-config-next": "12.0.1",
    "ethereum-waffle": "^3.0.0",
    "ethers": "^5.0.0"
  }

hardhat - Ethereum 开发环境
web3modal - 方便快速的连接钱包
react-markdown and simplemde - Markdown editor and markdown renderer for the CMS
@emotion/css - A great CSS in JS library
@openzeppelin/contracts -开源的solidity框架

#安装包依赖
npm install

准备hardhat部署脚本


npx hardhat

#选 Create an empty hardhat.config.js

开始编码

修改 styles/globals.css 文件,具体代码参考github,不贴了

public 文件夹添加 logo.svg and right-arrow.svg 

智能合约

// contracts/Blog.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "hardhat/console.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract Blog {
    string public name;
    address public owner;

    using Counters for Counters.Counter;
    Counters.Counter private _postIds;

    struct Post {
      uint id;
      string title;
      string content;
      bool published;
    }
    /* mappings can be seen as hash tables */
    /* here we create lookups for posts by id and posts by ipfs hash */
    mapping(uint => Post) private idToPost;
    mapping(string => Post) private hashToPost;

    /* events facilitate communication between smart contractsand their user interfaces  */
    /* i.e. we can create listeners for events in the client and also use them in The Graph  */
    event PostCreated(uint id, string title, string hash);
    event PostUpdated(uint id, string title, string hash, bool published);

    /* when the blog is deployed, give it a name */
    /* also set the creator as the owner of the contract */
    constructor(string memory _name) {
        console.log("Deploying Blog with name:", _name);
        name = _name;
        owner = msg.sender;
    }

    /* updates the blog name */
    function updateName(string memory _name) public {
        name = _name;
    }

    /* transfers ownership of the contract to another address */
    function transferOwnership(address newOwner) public onlyOwner {
        owner = newOwner;
    }

    /* fetches an individual post by the content hash */
    function fetchPost(string memory hash) public view returns(Post memory){
      return hashToPost[hash];
    }

    /* creates a new post */
    function createPost(string memory title, string memory hash) public onlyOwner {
        _postIds.increment();
        uint postId = _postIds.current();
        Post storage post = idToPost[postId];
        post.id = postId;
        post.title = title;
        post.published = true;
        post.content = hash;
        hashToPost[hash] = post;
        emit PostCreated(postId, title, hash);
    }

    /* updates an existing post */
    function updatePost(uint postId, string memory title, string memory hash, bool published) public onlyOwner {
        Post storage post =  idToPost[postId];
        post.title = title;
        post.published = published;
        post.content = hash;
        idToPost[postId] = post;
        hashToPost[hash] = post;
        emit PostUpdated(post.id, title, hash, published);
    }

    /* fetches all posts */
    function fetchPosts() public view returns (Post[] memory) {
        uint itemCount = _postIds.current();

        Post[] memory posts = new Post[](itemCount);
        for (uint i = 0; i < itemCount; i++) {
            uint currentId = i + 1;
            Post storage currentItem = idToPost[currentId];
            posts[i] = currentItem;
        }
        return posts;
    }

    /* this modifier means only the contract owner can */
    /* invoke the function */
    modifier onlyOwner() {
      require(msg.sender == owner);
    _;
  }
}

合约允许拥有者创建,编辑博客内容,允许任何人获取内容

测试合约 

test/sample-test.js

onst { expect } = require("chai")
const { ethers } = require("hardhat")

describe("Blog", async function () {
  it("Should create a post", async function () {
    const Blog = await ethers.getContractFactory("Blog")
    const blog = await Blog.deploy("My blog")
    await blog.deployed()
    await blog.createPost("My first post", "12345")

    const posts = await blog.fetchPosts()
    expect(posts[0].title).to.equal("My first post")
  })

  it("Should edit a post", async function () {
    const Blog = await ethers.getContractFactory("Blog")
    const blog = await Blog.deploy("My blog")
    await blog.deployed()
    await blog.createPost("My Second post", "12345")

    await blog.updatePost(1, "My updated post", "23456", true)

    posts = await blog.fetchPosts()
    expect(posts[0].title).to.equal("My updated post")
  })

  it("Should add update the name", async function () {
    const Blog = await ethers.getContractFactory("Blog")
    const blog = await Blog.deploy("My blog")
    await blog.deployed()

    expect(await blog.name()).to.equal("My blog")
    await blog.updateName('My new blog')
    expect(await blog.name()).to.equal("My new blog")
  })
})
npx hardhat test

 部署合约

部署前先启动本地eth网络

npx hardhat node

启动成功后,可以看到20个测试账号,后续测试开发可以用

修改部署脚本 scripts/deploy.js

/* scripts/deploy.js */
const hre = require("hardhat");
const fs = require('fs');

async function main() {
  /* these two lines deploy the contract to the network */
  const Blog = await hre.ethers.getContractFactory("Blog");
  const blog = await Blog.deploy("My blog");

  await blog.deployed();
  console.log("Blog deployed to:", blog.address);

  /* this code writes the contract addresses to a local */
  /* file named config.js that we can use in the app */
  fs.writeFileSync('./config.js', `
  export const contractAddress = "${blog.address}"
  export const ownerAddress = "${blog.signer.address}"
  `)
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

执行部署

npx hardhat run scripts/deploy.js --network localhost

 部署成功,合约地址:0x5fbdb2315678afecb367f032d93f642f64180aa3 

meta钱包

前面创建的地址,选一个

Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

添加网络

导入账号 ,前面选择的秘钥0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

查看余额

Next.js app 

环境配置文件

先创建环境配置文件.env.local

ENVIRONMENT="local"
NEXT_PUBLIC_ENVIRONMENT="local"

变量可以切换localtestnet, and mainnet

js代码对应

context.js

import { createContext } from 'react'

export const AccountContext = createContext(null)

Layout and Nav

打开pages/_app.js ,修改,参考github代码

Entrypoint入口页面

打开pages/index.js,参考github代码

发布博客页面 

pages/create-post.js,参考github代码

查看博客内容页面

博客的详情地址规则,myapp.com/post/some-post-id,修改文件pages/post/[id].js

编辑博客内容

修改文件 pages/post/[id].js

调试运行

npm run dev

或者使用vscode调试

launch.json

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch via npm",
            "type": "node",
            "request": "launch",
            "cwd": "${workspaceFolder}",
            "runtimeExecutable": "npm",
            "runtimeArgs": ["run-script", "dev"]
          }
          
    ]
}

执行失败,报错

Error: could not detect network (event="noNetwork", code=NETWORK_ERROR, version=providers/5.7.2)

搜索代码

    provider = new ethers.providers.JsonRpcProvider()

#改为

    provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545/')

这个127.0.0.1:8545 对应前面的 hardhat 网络

保存后,又报错

Error: Invalid <Link> with <a> child. Please remove <a> or use <Link legacyBehavior>.

搜索代码<a ,对应找到<Link 后面加 legacyBehavior

 跑起来了

连接metamask钱包

发帖

 失败了,看了一下infura doc, Make requests - Infura Docs,使用的不太对,改一下,

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

改为

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

/* define the ipfs endpoint */
const client = create({
  host: 'ipfs.infura.io',
  port: 5001,
  protocol: 'https',
  headers: {
      authorization: auth,
  },
})

代码的xxxx是infura里申请的

搞定

查看帖子又报错

debug发现多了一个/,去掉

浏览器能访问了,但是代码还不行,查了一下infura的doc,Public gateway 已经关闭了,需要用infura上创建项目的gateway,具体原因:Public gateway - Infura Docs

const ipfsURI = 'https://ipfs.io/ipfs'

#改为

const ipfsURI = 'https://xxx.infura-ipfs.io/ipfs'

# xxx是你自己的gateway

上polygon

meta钱包

Chainlist

 水龙头来点钱

Polygon Faucet

部署

hardhat.config.js 打开注释

require("@nomiclabs/hardhat-waffle")

/** @type import('hardhat/config').HardhatUserConfig */

module.exports = {
  solidity: "0.8.17",
  networks:{
    hardhat:{
      chainId:1337
    },
    mumbai: {
      url: "https://polygon-mumbai.blockpi.network/v1/rpc/public",
      accounts: ["ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"]
    },
    // polygon: {
    //   url: "https://polygon-rpc.com/",
    //   accounts: [process.env.pk]
    // }
  }

这里的ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 是钱包私钥,前面生成的 

npx hardhat run scripts/deploy.js --network mumbai

url: "https://polygon-mumbai.blockpi.network/v1/rpc/public",

可以从Chainlist

找一个

部署成功

➜  web3-blog git:(main) ✗ npx hardhat run scripts/deploy.js --network mumbai

Blog deployed to: 0x81FeD4CdB0609bE8a23Bc5B95d875c05DD9416E8

花了些测 

运行next

修改 .env.local   ,local 改为 testnet

ENVIRONMENT="testnet"
NEXT_PUBLIC_ENVIRONMENT="testnet"

源码中 https://rpc-mumbai.matic.today 已经不可用,改为https://polygon-mumbai.blockpi.network/v1/rpc/public

npm run dev

跑起来

subgraph

源码中fetchPost and fetchPosts,可以查看某个文章或者全部文章,如果想要搜索文章怎么弄?

The Graph 协议可以实现这个功能

创建subgraph

通过  Graph 命令行来初始化subgraph

本机执行命令

npm install -g @graphprotocol/graph-cli
#命令参考
graph init --from-contract your-contract-address \
--network mumbai --contract-name Blog --index-events


#具体命令
graph init --from-contract 0x81FeD4CdB0609bE8a23Bc5B95d875c05DD9416E8 \
--network mumbai --contract-name Blog --index-events

  • subgraph.yaml: subgraph 的配置文件
  • schema.graphql: GraphQL 语法文件,定义了数据存储和访问
  • AssemblyScript Mappings: schema.ts AssemblyScript code that translates from the event data in Ethereum to the entities defined in your schema (e.g. mapping.ts in this tutorial)

subgraph.yaml

  • description (optional): a human-readable description of what the subgraph is. This description is displayed by the Graph Explorer when the subgraph is deployed to the Hosted Service.
  • repository (optional): the URL of the repository where the subgraph manifest can be found. This is also displayed by the Graph Explorer.
  • dataSources.source: the address of the smart contract the subgraph sources, and the abi of the smart contract to use. The address is optional; omitting it allows to index matching events from all contracts.
  • dataSources.source.startBlock (optional): the number of the block that the data source starts indexing from. In most cases we suggest using the block in which the contract was created.
  • dataSources.mapping.entities : the entities that the data source writes to the store. The schema for each entity is defined in the the schema.graphql file.
  • dataSources.mapping.abis: one or more named ABI files for the source contract as well as any other smart contracts that you interact with from within the mappings.
  • dataSources.mapping.eventHandlers: lists the smart contract events this subgraph reacts to and the handlers in the mapping — ./src/mapping.ts in the example — that transform these events into entities in the store.

定义 entities

在 schema.graphql  里定义entity, Graph Node将会生成包括entity的查询实例。每个类型必须是entity,通过 @entity 声明

entities / data 将会对 Token 和 User 进行索引。通过这个方法我们可以对用户(user)创建的Tokens 进行索引

schema.graphql 

type _Schema_
  @fulltext(
    name: "postSearch"
    language: en
    algorithm: rank
    include: [{ entity: "Post", fields: [{ name: "title" }, { name: "postContent" }] }]
  )

type Post @entity {
  id: ID!
  title: String!
  contentHash: String!
  published: Boolean!
  postContent: String!
  createdAtTimestamp: BigInt!
  updatedAtTimestamp: BigInt!
}

通过命令行生成

cd blogcms
graph codegen

更新 subgraph 的 entity 和 mappings

subgraph.yaml 

 Assemblyscript mappings

import {
  PostCreated as PostCreatedEvent,
  PostUpdated as PostUpdatedEvent
} from "../generated/Blog/Blog"
import {
  Post
} from "../generated/schema"
import { ipfs, json } from '@graphprotocol/graph-ts'

export function handlePostCreated(event: PostCreatedEvent): void {
  let post = new Post(event.params.id.toString());
  post.title = event.params.title;
  post.contentHash = event.params.hash;
  let data = ipfs.cat(event.params.hash);
  if (data) {
    let value = json.fromBytes(data).toObject()
    if (value) {
      const content = value.get('content')
      if (content) {
        post.postContent = content.toString()
      }
    }
  }
  post.createdAtTimestamp = event.block.timestamp;
  post.save()
}

export function handlePostUpdated(event: PostUpdatedEvent): void {
  let post = Post.load(event.params.id.toString());
  if (post) {
    post.title = event.params.title;
    post.contentHash = event.params.hash;
    post.published = event.params.published;
    let data = ipfs.cat(event.params.hash);
    if (data) {
      let value = json.fromBytes(data).toObject()
      if (value) {
        const content = value.get('content')
        if (content) {
          post.postContent = content.toString()
        }
      }
    }
    post.updatedAtTimestamp = event.block.timestamp;
    post.save()
  }
}

重新编译

graph build

Deploying the subgraph

找到subgraph的token

 

graph auth --product hosted-service 你的suggraph的key

部署

yarn deploy

查询

{
  posts {
    id
    title
    contentHash
    published
    postContent
  }
}

没有数据,我们发个帖

 

链上查询

Contract Address 0x81FeD4CdB0609bE8a23Bc5B95d875c05DD9416E8 | PolygonScan

不过 suggraph还是没数据

 查一下log,出错了

 看了一下说明,schema.graphql应该是定义entity的published时候用了!,这个是强制不能为空的,而handlePostCreated里是没有published参数的,去掉!,再试试

type _Schema_
  @fulltext(
    name: "postSearch"
    language: en
    algorithm: rank
    include: [{ entity: "Post", fields: [{ name: "title" }, { name: "postContent" }] }]
  )

type Post @entity {
  id: ID!
  title: String
  contentHash: String
  published: Boolean
  postContent: String
  createdAtTimestamp: BigInt
  updatedAtTimestamp: BigInt
}

参考Creating a Subgraph - The Graph Docs

重新编译,上传

graph codegen
graph build
yarn deploy

也可以用部署成功后的url来查询

 

来搜索个文章

{
  postSearch(
    text: "111"
  ) {
    id
    title
    contentHash
    published
    postContent
  }
}

 

app使用graph

参考Querying from an Application - The Graph Docs 

这里源项目没有,我加了个搜索功能,react不熟悉,看着弄吧 

全局添加命令行

npm install --save-dev @graphprotocol/client-cli

package.json

里添加

    "@apollo/client": "^3.7.12",
npm install

vscode

添加插件

search.js

直接写死搜索111

import { useState, useEffect } from 'react'
import { ApolloClient, InMemoryCache, gql } from '@apollo/client'

// https://api.thegraph.com/subgraphs/name/daocodedao/blogcms
const APIURL = 'https://api.thegraph.com/subgraphs/name/daocodedao/blogcms'

const blogQuery = `
{
    postSearch(
      text: "111"
    ) {
      id
      title
      contentHash
      published
      postContent
    }
}
`

const client = new ApolloClient({
  uri: APIURL,
  cache: new InMemoryCache(),
})

export default function Search() {
    const [searchCount, setSearchCount] = useState(0)
    client.query({
        query: gql(blogQuery),
    })
    .then((data) => {
        console.log('Subgraph data: ', data)
        setSearchCount(data?.data?.postSearch?.length)
    })
    .catch((err) => {
        console.log('Error fetching data: ', err)
    })
    return (
        <div>搜索条件是:111, 共有
        {
            searchCount
        }
        条数据
        </div>
    )
}

 实在不熟React,搞了挺久的

题外话

在使用Next过程中,因为对整个框架不熟悉,提取infura key到.env 文件时遇到了一些问题

在.env.local文件里

INFURA_KEY="xxxxxxxxxxxxx"

在js代码里使用

process.env.INFURA_KEY

结果在console里是打印的是对的,在chrome里打印出undefined

解决方案:https://medium.com/@zak786khan/env-variables-undefined-78cf218dae87

在.env.local文件里,变量名改为


NEXT_PUBLIC_INFURA_KEY="xxxxxxxxxxxxx"

就行了 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/422970.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python pandas数据处理excel、csv列转行、行转列(具体示例)

一、数据处理需求 对Excel或CSV格式的数据,我们经常都是使用pandas库读取后转为DataFrame进行处理。有的时候我们需要对其中的数据进行行列转换,但是不是简单的行列转换,因为数据中有重复的数据属性。比如我们的数据在Excel中的格式如下: 那么,我们如何将上面的数据格式转…

2023爱分析·商业智能应用解决方案市场厂商评估报告:数聚股份

目录 1. 研究范围定义 2. 商业智能应用解决方案市场分析 3. 厂商评估&#xff1a;数聚股份 4. 入选证书 1. 研究范围定义 商业智能&#xff08;BI&#xff09;是在实现数据集成和统一管理的基础上&#xff0c;利用数据存储和处理、分析与展示等技术&#xff0c;满足企…

Java多线程案例-Java多线程(3)

各位观众老爷们, 创作不易, 多多支持&#x1f636;‍&#x1f32b;️&#x1f64f;&#x1f618; 字数11223, 时间:2023年4月16日11:19:58 状态: 精神恍恍惚惚, 想打游戏&#x1f975; 目录(点击传送) 单例模式 饿汉模式 懒汉模式 单线程版 多线程版 阻塞队列 什么是阻塞队…

助力工业物联网,工业大数据之ODS层及DWD层建表语法【七】

文章目录ODS层及DWD层构建01&#xff1a;课程回顾02&#xff1a;课程目标03&#xff1a;数仓分层回顾04&#xff1a;Hive建表语法05&#xff1a;Avro建表语法ODS层及DWD层构建 01&#xff1a;课程回顾 一站制造项目的数仓设计为几层以及每一层的功能是什么&#xff1f; ODS&am…

Pytorch-gpu的安装

1.先安装cuda和cudnn 推荐安装cuda11.3和cuda10.2&#xff0c;因为这两个版本用的多。 安装教程可以看我的另一篇文章&#xff1a; cuda和cudnn安装教程 2.安装对应版本的Pytorch Pytorch的版本需要和cuda的版本相对应。具体对应关系可以去官网查看。这里先附上一张对应关系…

openpnp - 顶部相机 - 辅助光(环形灯)的电路原理图

文章目录openpnp - 顶部相机 - 辅助光(环形灯)的电路原理图概述ENDopenpnp - 顶部相机 - 辅助光(环形灯)的电路原理图 概述 同学帮我做的简易灯板设计不太合理, 发热量极大. 想看看商用的环形灯电路啥样的, 如果有可能, 自己做块灯板, 塞进商用环形灯外壳中. 拆解了一个环形灯…

数据库备份

数据库备份&#xff0c;恢复实操 策略一&#xff1a;&#xff08;文件系统备份工具 cp&#xff09;&#xff08;适合小型数据库&#xff0c;是最可靠的&#xff09; 1、停止MySQL服务器。 2、直接复制整个数据库目录。注意&#xff1a;使用这种方法最好还原到相同版本服务器中&…

【图像分类】【深度学习】ViT算法Pytorch代码讲解

【图像分类】【深度学习】ViT算法Pytorch代码讲解 文章目录【图像分类】【深度学习】ViT算法Pytorch代码讲解前言ViT(Vision Transformer)讲解patch embeddingpositional embeddingTransformer EncoderEncoder BlockMulti-head attentionMLP Head完整代码总结前言 ViT是由谷歌…

Spring Boot+Vue实现Socket通知推送

目录 Spring Boot端 第一步&#xff0c;引入依赖 第二步&#xff0c;创建WebSocket配置类 第三步&#xff0c;创建WebSocket服务 第四步&#xff0c;创建Controller进行发送测试 Vue端 第一步&#xff0c;创建连接工具类 第二步&#xff0c;建立连接 ​编辑 第三步&a…

xxl-job-2.3.1 本地编译jar包并部署

参考网上其他文章&#xff0c;总结步骤 一、官网地址 分布式任务调度平台XXL-JOB 二、源码地址 github&#xff1a; GitHub - xuxueli/xxl-job: A distributed task scheduling framework.&#xff08;分布式任务调度平台XXL-JOB&#xff09; gitee: xxl-job: 一个分布…

k8s v1.26.2 安装部署步骤

准备 开通端口 master需要开通的端口: TCP: 6443 2379 2380 10250 10259 10257 ,10250 30000~30010(应用) node需要开通的端口: TCP: 10250 30000~30010(应用) master加端口 firewall-cmd --permanent --add-port6443/tcp firewall-cmd --permanent --add-port2379/tcp fir…

数据库学习笔记 概念数据库的设计(2)

回顾上节课的内容 数据库的设计:概念设计:抽取实体和联系 逻辑设计:设计模式 设计模式 物理设计:设计数据库的内模式 和存储和存取相关的配置 sql创建索引可以做分表 将所有学生存入一张表或者每个学院一个表 根据某种条件进行分表 CSstudent 实体联系模型 叫ER图 实体(矩形)和…

城乡供水信息化平台建设-构建智慧美丽乡村

建设方案 城乡供水信息化系统是运用云计算、大数据等信息化手段&#xff0c;借助在线监测设备&#xff0c;并依托“城乡供水信息化平台”&#xff0c;实时感知供水系统的运行状态&#xff0c;实现对农村供水工程远程监控、在线监测、实时预警、智慧监管。 系统功能 水源地监测&…

springboot+vue职称评审管理系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的职称评审管理系统。项目源码请联系风歌&#xff0c;文末附上联系信息 。 目前有各类成品java毕设&#xff0c;需要请看文末联系方式 …

OSPF出口设计

1.合理规划OSPF区域,使得办公网络发生变化时服务器区域(R7)路由表不会发生变化,写出两种方案,请详细描述该方案? OSPF区域规划如下: 核心路由器和汇聚路由器之间所有链路工作在区域0骨干区域,而办公网络接入路由器和汇聚路由器之间链路工作在区域10;服务器网络接入路由…

Java集合类源码阅读(一)

文章目录一. Iterator&#xff08;迭代器&#xff09;1. Iterator源码2. ListIterator源码二. Collection三. List四. Vector在阅读源码之前&#xff0c;我们首先需要知道&#xff0c;java集合的一个继承关系图&#xff0c;如下所示然后按照个集合关系图&#xff0c;逐步阅读源…

onnxruntime 运行过程报错“onnxruntime::Model::Model Unknown model file format version“

背景 这几天在玩一下yolov6&#xff0c;使用的是paddle框架训练的yolov6&#xff0c;然后使用paddl 转成onnx&#xff0c;再用onnxruntime来去预测模型。由于是在linux服务器上转出来 的onnx模型&#xff0c;并在本地的windows电脑上去使用&#xff0c;大概就是这样的一个情…

C语言中的小知识点(程序环境和预处理篇(1))

系列文章目录 文章目录系列文章目录[TOC](文章目录)前言一、编译链接是什么&#xff1f;&#xff08;一&#xff09;、翻译环境二、预处理的讲解1.预定义符号2.#define定义标识符总结前言 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境 第一种是翻译环境&#xff0c…

JavaWeb开发 —— HTML

目录 一、什么是HTML、CSS 二、内容介绍 三、基本标签 & 样式 1. 实现标题 1.1 标题排版 1.2 标题样式 1.3 超链接 2. 实现正文 2.1 正文排版 2.2 页面布局 四、表格标签 五、表单标签 1. 表单标签 2. 表单项标签 一、什么是HTML、CSS HTML ( HyperTex…

MAC 删除自带 ABC 输入法的方法

背景&#xff1a; 在使用 ​​mac​​​ 时相信很多都习惯使用第三方输入法&#xff0c;而 ​​mac​​​ 规定必须保留一个自带的 ​​ABC​​​ 输入法&#xff0c;这样导致平时在打字的时候&#xff0c;老是莫名其妙的自己切换成了自带的 ​​ABC​​​ 输入法&#xff0c;…