本文目录导读:

- 项目结构
- Maven依赖配置 (pom.xml)
- 配置文件 (application.yml)
- 配置类
- 数据模型
- IPFS服务
- 以太坊智能合约服务
- NFT服务
- Controller
- 主启动类
- 使用示例
- 智能合约示例(Solidity)
- 部署说明
我来为您提供一个完整的Java NFT案例,包含智能合约交互、铸币、查询等核心功能。
项目结构
nft-project/
├── pom.xml
└── src/
└── main/
└── java/
└── com/
└── example/
└── nft/
├── config/
├── model/
├── service/
└── controller/
Maven依赖配置 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>nft-project</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.14</version>
</dependency>
<!-- Web3j - Ethereum 交互 -->
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>4.9.8</version>
</dependency>
<!-- IPFS -->
<dependency>
<groupId>com.github.ipfs</groupId>
<artifactId>java-ipfs-http-client</artifactId>
<version>1.4.4</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<optional>true</optional>
</dependency>
<!-- 配置文件处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
配置文件 (application.yml)
server:
port: 8080
spring:
application:
name: nft-service
nft:
ethereum:
# RPC节点地址(本地或Infura)
rpc-url: ${RPC_URL:http://localhost:8545}
# 合约地址
contract-address: ${CONTRACT_ADDRESS:0x0000000000000000000000000000000000000000}
# 钱包私钥
private-key: ${PRIVATE_KEY:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
# Gas 设置
gas-price: 20000000000
gas-limit: 3000000
ipfs:
# IPFS节点地址
host: ${IPFS_HOST:localhost}
port: ${IPFS_PORT:5001}
# 合约ABI
contract-abi: |
[
{
"constant": false,
"inputs": [
{"name": "tokenURI", "type": "string"}
],
"name": "mintNFT",
"outputs": [
{"name": "", "type": "uint256"}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
]
配置类
package com.example.nft.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "nft.ethereum")
public class EthereumConfig {
private String rpcUrl;
private String contractAddress;
private String privateKey;
private Long gasPrice;
private Long gasLimit;
}
数据模型
package com.example.nft.model;
import lombok.Data;
import java.math.BigInteger;
@Data
public class NFTMetadata {
private String name;
private String description;
private String image;
private String externalUrl;
private String[] attributes;
}
@Data
public class NFTToken {
private BigInteger tokenId;
private String tokenURI;
private String owner;
private NFTMetadata metadata;
}
@Data
public class MintRequest {
private String name;
private String description;
private String image;
private String receiverAddress;
}
IPFS服务
package com.example.nft.service;
import io.ipfs.api.IPFS;
import io.ipfs.api.MerkleNode;
import io.ipfs.multihash.Multihash;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class IPFSService {
private final IPFS ipfs;
public IPFSService() {
// 连接IPFS节点
this.ipfs = new IPFS("localhost", 5001);
}
/**
* 上传文件到IPFS
*/
public String uploadFile(MultipartFile file) throws Exception {
byte[] content = file.getBytes();
MerkleFile addResult = ipfs.add(content);
return addResult.hash.toString();
}
/**
* 上传JSON元数据到IPFS
*/
public String uploadMetadata(String jsonMetadata) throws Exception {
byte[] content = jsonMetadata.getBytes();
MerkleNode addResult = ipfs.add(content);
return addResult.hash.toString();
}
/**
* 从IPFS获取内容
*/
public byte[] getContent(String hash) throws Exception {
Multihash fileHash = Multihash.fromBase58(hash);
return ipfs.cat(fileHash);
}
}
以太坊智能合约服务
package com.example.nft.service;
import com.example.nft.config.EthereumConfig;
import com.example.nft.model.NFTMetadata;
import com.example.nft.model.NFTToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.web3j.abi.DefaultFunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Uint;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Slf4j
@Service
public class EthereumService {
private final Web3j web3j;
private final Credentials credentials;
private final EthereumConfig config;
private final ObjectMapper objectMapper;
public EthereumService(EthereumConfig config, ObjectMapper objectMapper) {
this.config = config;
this.objectMapper = objectMapper;
this.web3j = Web3j.build(new HttpService(config.getRpcUrl()));
this.credentials = Credentials.create(config.getPrivateKey());
}
/**
* 铸币(Mint)NFT
*/
public String mintNFT(String receiverAddress, String tokenURI) throws Exception {
try {
// 构造mintNFT函数调用
Function function = new Function(
"mintNFT",
Arrays.asList(new Utf8String(tokenURI)),
Collections.singletonList(new TypeReference<Uint>() {})
);
// 生成交易数据
String encodedFunction = DefaultFunctionReturnDecoder.encodeFunction(function);
// 获取nonce
BigInteger nonce = getNonce(credentials.getAddress());
// 获取gas价格
EthGasPrice gasPrice = web3j.ethGasPrice().send();
// 创建交易
Transaction transaction = Transaction.createFunctionCallTransaction(
credentials.getAddress(), // from
nonce, // nonce
gasPrice.getGasPrice(), // gas price
BigInteger.valueOf(3000000), // gas limit
config.getContractAddress(), // to - 合约地址
encodedFunction // data
);
// 签名并发送交易
signedTransaction signedTransaction = web3j.ethAccountSign(
transaction, credentials).send();
// 发送交易
EthSendTransaction ethSendTransaction = web3j.ethSendTransaction(transaction).send();
if (ethSendTransaction.hasError()) {
throw new RuntimeException("Transaction failed: " +
ethSendTransaction.getError().getMessage());
}
log.info("NFT minted successfully. TxHash: {}",
ethSendTransaction.getTransactionHash());
return ethSendTransaction.getTransactionHash();
} catch (Exception e) {
log.error("Failed to mint NFT", e);
throw e;
}
}
/**
* 获取NFT URI
*/
public String getTokenURI(BigInteger tokenId) throws Exception {
Function function = new Function(
"tokenURI",
Arrays.asList(new Uint(tokenId)),
Collections.singletonList(new TypeReference<Utf8String>() {})
);
String encodedFunction = DefaultFunctionReturnDecoder.encodeFunction(function);
Transaction transaction = Transaction.createEthCallTransaction(
credentials.getAddress(),
config.getContractAddress(),
encodedFunction
);
EthCall ethCall = web3j.ethCall(
transaction,
DefaultBlockParameterName.LATEST
).send();
if (ethCall.hasError()) {
throw new RuntimeException("Call failed: " +
ethCall.getError().getMessage());
}
String decodedValue = DefaultFunctionReturnDecoder.decodeFunctionResult(
function, ethCall.getValue()
).get(0).getValue().toString();
return decodedValue;
}
/**
* 获取当前nonce
*/
private BigInteger getNonce(String address) throws Exception {
return web3j.ethGetTransactionCount(
address,
DefaultBlockParameterName.LATEST
).send().getTransactionCount();
}
}
NFT服务
package com.example.nft.service;
import com.example.nft.model.MintRequest;
import com.example.nft.model.NFTMetadata;
import com.example.nft.model.NFTToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigInteger;
@Slf4j
@Service
public class NFTService {
private final IPFSService ipfsService;
private final EthereumService ethereumService;
private final ObjectMapper objectMapper;
public NFTService(IPFSService ipfsService,
EthereumService ethereumService,
ObjectMapper objectMapper) {
this.ipfsService = ipfsService;
this.ethereumService = ethereumService;
this.objectMapper = objectMapper;
}
/**
* 创建NFT(铸币)
*/
public String createNFT(MintRequest request, MultipartFile image) throws Exception {
log.info("Creating NFT: {}", request.getName());
// 1. 上传图片到IPFS
String imageHash = ipfsService.uploadFile(image);
String imageUrl = "ipfs://" + imageHash;
// 2. 创建元数据
NFTMetadata metadata = new NFTMetadata();
metadata.setName(request.getName());
metadata.setDescription(request.getDescription());
metadata.setImage(imageUrl);
// 3. 上传元数据到IPFS
String metadataJson = objectMapper.writeValueAsString(metadata);
String metadataHash = ipfsService.uploadMetadata(metadataJson);
String tokenURI = "ipfs://" + metadataHash;
// 4. 调用智能合约铸币
String txHash = ethereumService.mintNFT(
request.getReceiverAddress(),
tokenURI
);
log.info("NFT created successfully. TxHash: {}", txHash);
return txHash;
}
/**
* 获取NFT信息
*/
public NFTToken getNFT(BigInteger tokenId) throws Exception {
log.info("Getting NFT info for token: {}", tokenId);
// 1. 从链上获取tokenURI
String tokenURI = ethereumService.getTokenURI(tokenId);
// 2. 从IPFS获取元数据
String hash = tokenURI.replace("ipfs://", "");
byte[] metadataBytes = ipfsService.getContent(hash);
NFTMetadata metadata = objectMapper.readValue(
metadataBytes,
NFTMetadata.class
);
// 3. 组装返回
NFTToken token = new NFTToken();
token.setTokenId(tokenId);
token.setTokenURI(tokenURI);
token.setMetadata(metadata);
return token;
}
}
Controller
package com.example.nft.controller;
import com.example.nft.model.MintRequest;
import com.example.nft.model.NFTToken;
import com.example.nft.service.NFTService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/nft")
public class NFTController {
private final NFTService nftService;
public NFTController(NFTService nftService) {
this.nftService = nftService;
}
/**
* 创建NFT
*/
@PostMapping("/mint")
public ResponseEntity<Map<String, String>> mintNFT(
@RequestParam("name") String name,
@RequestParam("description") String description,
@RequestParam("image") MultipartFile image,
@RequestParam("receiverAddress") String receiverAddress) {
try {
MintRequest request = new MintRequest();
request.setName(name);
request.setDescription(description);
request.setReceiverAddress(receiverAddress);
String txHash = nftService.createNFT(request, image);
Map<String, String> response = new HashMap<>();
response.put("status", "success");
response.put("transactionHash", txHash);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("Failed to mint NFT", e);
Map<String, String> response = new HashMap<>();
response.put("status", "error");
response.put("message", e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}
/**
* 查询NFT
*/
@GetMapping("/{tokenId}")
public ResponseEntity<NFTToken> getNFT(@PathVariable BigInteger tokenId) {
try {
NFTToken token = nftService.getNFT(tokenId);
return ResponseEntity.ok(token);
} catch (Exception e) {
log.error("Failed to get NFT", e);
return ResponseEntity.internalServerError().build();
}
}
/**
* 健康检查
*/
@GetMapping("/health")
public ResponseEntity<Map<String, String>> healthCheck() {
Map<String, String> response = new HashMap<>();
response.put("status", "NFT Service is running");
return ResponseEntity.ok(response);
}
}
主启动类
package com.example.nft;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties
public class NFTApplication {
public static void main(String[] args) {
SpringApplication.run(NFTApplication.class, args);
System.out.println("NFT Service started successfully!");
}
}
使用示例
1 创建NFT(curl示例)
curl -X POST http://localhost:8080/api/nft/mint \ -F "name=My First NFT" \ -F "description=This is my first NFT on blockchain" \ -F "image=@/path/to/your/image.png" \ -F "receiverAddress=0xYourWalletAddress"
2 查询NFT
curl http://localhost:8080/api/nft/1
智能合约示例(Solidity)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MyNFT is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
// IPFS路径映射
mapping(uint256 => string) private _tokenURIs;
constructor() ERC721("MyNFT", "MNFT") {}
function mintNFT(address recipient, string memory tokenURI)
public
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
return _tokenURIs[tokenId];
}
function _setTokenURI(uint256 tokenId, string memory tokenURI)
internal
{
_tokenURIs[tokenId] = tokenURI;
}
}
部署说明
-
环境准备:
- 安装Ganache(本地以太坊模拟器)
- 安装IPFS节点
- 部署智能合约
-
启动服务:
# 编译并运行 mvn clean package java -jar target/nft-project-1.0.0.jar
-
配置环境变量:
export RPC_URL=http://localhost:8545 export CONTRACT_ADDRESS=0x你的合约地址 export PRIVATE_KEY=你的私钥
这个完整的NFT案例涵盖了:
- 与以太坊智能合约的交互
- IPFS去中心化存储
- NFT元数据管理
- 铸币(Mint)功能
- RESTful API接口
您可以根据实际需求调整代码,比如添加NFT交易市场功能、拍卖功能等。