在spring boot下如何通过rest 接口 来上传文件 和下载文件 到 hadoop hdfs

springboot restful 上传文件 bezkoder: spring-boot-file-upload spring document: MultipartFile 通过restful api的方式,通过postman来上传文件,如: 图片,视频等多媒体文件 核心 MultipartFile 多媒体文件的抽象表示 @RequestParam(“name”) 在controller层不封装成对象时直接使用 @ModelAttribute 在controller层封装成对象时使用 @RequestParam | MultipartFile @ 阅读详情

本文将用代码来演示在spring boot里面,用hadoop client,通过restful API来上传文件 和下载文件 到 hadoop hdfs。

里面有一些代码依赖坑,注意绕行。

前提:

如果你的代码在windows上运行,去连接linux上的hadoop(2.7.x或者以上),则需要做一下设置。

1:下载下面的windows hadoop

https://github.com/steveloughran/winutils/tree/master/hadoop-2.8.1

在整个目录下载下来,放到一个目录下,比如 D:\thb\tools\hadoop-2.8.1-bin-master,然后设置环境变量

HADOOP_HOME=D:\thb\tools\hadoop-2.8.1-bin-master

PATH=%PATH%;%HADOOP_HOME%

如何设置请自行百度一下如何在windows里面设置系统环境变量。

2:pom.xml (注意hadoop-client,commons-io,commons-fileupload版本,否则编译或者运行会报错)

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.restdocs</groupId>
            <artifactId>spring-restdocs-mockmvc</artifactId>
            <scope>test</scope>
        </dependency><dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.4</version>
            <classifier>jdk15</classifier>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.7.2</version>
        </dependency>
    </dependencies>

代码如下

FileController.java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.multipart.commons.*;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
@RestController
@RequestMapping("/FileMgn")
public class FileController {
    private static final Logger log = LoggerFactory.getLogger(FileController.class);

    @Value("${fileUpload.path}")
    private String FILE_PATH;

    @Value("${hdfs.URILocation}")
    private String HDFS_URI;

    @Value("${fileUpload.tmpDir}")
    private String TMPDIR;  @RequestMapping(value = "/HDFSFileUpload")
    public String HDFSupload(@RequestParam("file") MultipartFile file) {
        String fileName = null;
        try {
            if (file.isEmpty()) {
                log.error("file name is Null!");
               String retmsg = "{\"ret\":\"200\",\"msg\":\"file name is required\"}";
                return retmsg;
            }
            fileName = file.getOriginalFilename();
            String path = TMPDIR + fileName;
            File dest = new File(path);
            if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();// if not, create parent directories recursively
            }
            //save to tmp directory firstly, then upload to HDFS.
            file.transferTo(dest);
        HdfsFileSystem.copyFileToHDFSByFileObj(dest, HDFS_URI + "/" + fileName);
            dest.delete();
            log.info(fileName + " uploaded to HDFS successfully");
            String retmsg = "{\"ret\":\"200\",\"msg\":\"" + fileName + " uploaded successfully" + "\"}";
            return retmsg;
        } catch (IllegalStateException e) {
            log.error(" exception happens: " + e.getMessage());
        } catch (IOException e) {
            log.error(" exception happens: " + e.getMessage());
        }
        log.error("uploading " + fileName + " to HDFS fails");
        String retmsg = "{\"ret\":\"200\",\"msg\":\"" + fileName + " upload fails" + "\"}";
        return retmsg;
    }
@PostMapping("/HDFSBatchUpload")
    public String handleHDFSFileUpload(HttpServletRequest request) {
        List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("file");
        MultipartFile file = null;
        BufferedOutputStream stream = null;
        for (int i = 0; i < files.size(); ++i) {
            file = files.get(i);
            if (!file.isEmpty()) {
                try {
                    String fileName = file.getOriginalFilename();
                    String path = TMPDIR + fileName;
                    File dest = new File(path);
                    if (!dest.getParentFile().exists()) {
                        dest.getParentFile().mkdirs();
                    }
file.transferTo(dest);
                    HdfsFileSystem.copyFileToHDFSByFileObj(dest, HDFS_URI + "/" + fileName);
                    dest.delete();
                    log.info("file " + fileName + " uploaded to HDFS successfully.");
                } catch (Exception e) {
                    stream = null;
                    log.error(file.getOriginalFilename() + " uploads fails. exception: " + e.getMessage());
                    String retmsg = "{\"ret\":\"200\",\"msg\":\"" + file.getOriginalFilename() + " uploaded to HDFS fail.\"}";
                    return retmsg;
                }
            } else {
                log.error(file.getOriginalFilename() + " content is empty.Skip it");
            }
        }
        String retmsg = "{\"ret\":\"200\",\"msg\":\"" + "files batch-uploading succeeds\"}";
        return retmsg;
    }

 @RequestMapping(value = "/HDFSFileDownload")
    public String downloadHDFSFile(HttpServletRequest request, HttpServletResponse response) {
        String fileName = null;
        String submitMehtod = request.getMethod();
        if (submitMehtod.equals("GET")) {
            fileName = request.getParameter("file");
        } else {  // POST method not supported yet
            log.error("POST method not supported");
            String retmsg = "{\"ret\":\"200\",\"msg\":\"" + "POST not supported yet\"}";
            return retmsg;
        }
        if (null == fileName) {
            log.error("file name is null!");
            String retmsg = "{\"ret\":\"200\",\"msg\":\"" + "file name is required!\"}";
            return retmsg;
        }
if (!HdfsFileSystem.exists(HDFS_URI, fileName)) {
            log.error(HDFS_URI + "/" + fileName + " not exist in HDFS ");
            String retmsg = "{\"ret\":\"200\",\"msg\":\"" + fileName + " not exist\"}";
            return retmsg;
        }
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Expires", "0");
        response.setHeader("charset", "utf-8");
        response.setContentType("application/force-download");//force not to open downloaded file
        response.setHeader("Content-Transfer-Encoding", "binary");
        response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
 OutputStream os = null;
        try {
            os = response.getOutputStream();
            HdfsFileSystem.downloadFromHDFS(HDFS_URI, fileName, os);
        } catch (IOException e) {
            String retmsg = "{\"ret\":\"200\",\"msg\":\"" + fileName + " download failed \"}";
            log.error(fileName + " downloading failed. exception:" + e.getMessage());
            return retmsg;
        }
        log.info(fileName + " download successfully");
        String retmsg = "{\"ret\":\"200\",\"msg\":\"" + fileName + " download fails\"}";
        return retmsg;
    }
}

HdfsFileSystem.java

import java.io.*;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HdfsFileSystem {
    private static final Logger log = LoggerFactory.getLogger(HdfsFileSystem.class);
    
    public static void copyFileToHDFSByName(Configuration conf , String hdfsUri, String localFileName, String remoteFileName) throws IOException {
        FileSystem fs = FileSystem.get(URI.create(hdfsUri), conf);
        fs.copyFromLocalFile(new Path(localFileName), new Path(remoteFileName));
        System.out.println("copy from local file:" + localFileName + " to HDFS file:" + remoteFileName +" done.");
        log.info("copy from local file:" + localFileName + " to HDFS file:" + remoteFileName +" done.");
        fs.close();
    }
public static void copyFileToHDFSByFileObj(File localPath, String hdfsPath) throws IOException {
        InputStream in = null;
        if(null==localPath || null==hdfsPath || hdfsPath.isEmpty()){
            log.warn("copyFileToHDFSByFile: localpath and hdfspath are required");
            return;
        }
        try {
            Configuration conf = new Configuration();
            FileSystem fileSystem = FileSystem.get(URI.create(hdfsPath), conf);
            FSDataOutputStream out = fileSystem.create(new Path(hdfsPath));
            in = new BufferedInputStream(new FileInputStream(localPath));
            IOUtils.copyBytes(in, out, 4096, false);
            out.hsync();
            out.close();
            //in.close();
        }
        finally {
            IOUtils.closeStream(in);
        }
        return;
    }
 /*
    * Download hdfs file in URI to local file
    */
    public static void downloadFromHDFS(Configuration conf , String uri ,String remoteFileName, String localFileName) throws IOException {
        Path path = new Path(remoteFileName);
        FileSystem fs = FileSystem.get(URI.create(uri), conf);
        fs.copyToLocalFile(path, new Path(localFileName));
        fs.close();
        System.out.println("downloading file from " + remoteFileName + " to " + localFileName +" succeed");
        log.info("downloading file from " + remoteFileName + " to " + localFileName +" succeed");
        return;
    }
 /*
     * Download hdfs file in URI to local file
     */
    public static void downloadFromHDFS(String uri ,String HDFSFileName, OutputStream localFileOutPut) throws IOException {
        Configuration config=new Configuration();
        FileSystem fs = FileSystem.get(URI.create(uri),config);
        InputStream is = fs.open(new Path(uri+"/"+HDFSFileName));
        IOUtils.copyBytes(is,localFileOutPut,4096, true);//close in and out stream via this API itself.
        System.out.println("downloading HDFS file " + HDFSFileName + " succeed");
        log.info("downloading HDFS file " + HDFSFileName + " succeed");
        fs.close();
        return;
    }
 /*
   * check whether the HDFS file exists in given URI
   */
    public static boolean  exists(String HDFSUri,String HDFSFileName) {
        Configuration conf = new Configuration();
        boolean fileExists=false;
        try {
            FileSystem fileSystem = FileSystem.get(URI.create(HDFSUri), conf);
            fileExists=fileSystem.exists(new Path(HDFSUri+"/"+HDFSFileName));
        } catch (IOException e){
            log.error("hdfs:exist() exception occurs. exception:" + e.getMessage());
            return fileExists;
        }

        System.out.println("HDFS URI:" +HDFSUri+", fileName:"+HDFSFileName +" exists ? "+fileExists);
        log.info("HDFS URI:" +HDFSUri+",fileName:"+HDFSFileName +" exists ? "+fileExists);
        return fileExists;
    }
}

一些有用的参考文档

https://blog.youkuaiyun.com/dopamy_busymonkey/article/details/51036248

https://github.com/steveloughran/winutils

https://blog.youkuaiyun.com/u014695188/article/details/52398239

 

 

Hadoop+Spring Boot+爬虫:招聘信息分析系统实战全解析 数据挖掘是发现数据中潜在规律的核心手段,其落地依赖于海量数据的有效存储、清洗与计算。分布式计算框架Hadoop提供了可靠的HDFS存储与MapReduce离线处理能力,而Spring Boot作为成熟的后端服务框架,能够快速构建数据查询与可视化接口。在工程实践中,结合爬虫技术采集半结构化的网络数据,再经过数据清洗、聚类分析和关联规则挖掘,可以形成一条完整的“数据采集→存储计算→服务展示”链路。招聘信息分析正是这类技术架构的典型应用场景:面对岗位数据量大、字段杂、噪声高等挑战,借助Hadoop实现分布式清洗与 阅读详情

相关推荐

spring boot restful 接口示例项目

spring boot restful 接口示例项目,很详细,希望对大家有帮助。

文件上传下载rest实现

REST的应用越来越广泛,网站看见一个关于这个的实现,没有细看,转到这里,以后研究一下,不过这个示例代码与业务方面如何松耦合需要重新考虑和设计。 调用的客户端可以参考:http://bbs.kuaipan.cn/forum.php?mod=viewthread&tid=88347这里的实现,一会儿我也会转发。 转自:http://www.blogjava.net/qingchenghuan

1693

springmvc+hadoop+maven实现文件上传至hdfs

本实例依托于springmvc框架,采用maven作为jar包管理仓库,通过引用hadoop相关jar包,编写了hdfs操作工具类,能够在web端实现文件远程上传至hdfs。

springboot 整合Hadoop

springboot整合Hadoop

xiaobaihaob的博客 1438

(源码)基于Spring Boot和Hadoop的物品租赁系统.zip

# 基于Spring Boot和Hadoop的物品租赁系统 ## 项目简介 本项目是基于Spring Boot框架和Hadoop的物品租赁系统,提供物品租赁相关的各类功能,满足管理员和用户在物品租赁业务中的不同需求。系统具备良好的安全性、可维护性、适应性和可靠性,能在高并发情况下保持优越的运行速度和容错能力。 ## 项目的主要特性和功能 1. 管理员端包含用户管理、公告类型管理、公告信息管理、物品类别管理、物品信息管理、物品租赁管理、物品归还管理、评价信息管理、物品出租管理、系统管理等功能。 2. 用户端主要有个人中心、物品租赁管理、物品归还管理、评价信息管理、我的收藏管理等功能。 3. 通用功能支持文件上传与下载功能,文件上传时可保存到本地并同步上传到Hadoop HDFS。同时项目还提供爬虫、通用数据查询统计等功能。 ## 安装使用步骤(假设已下载项目源码文件) 1. 配置开发环境 配置Java开发环境,确保使用JDK1.8 。

阿里云大模型ACP认证模拟考试(三)

【摘要】:在阿里云大模型高级工程师 ACP 认证暂无真题题库的现状下,模拟题具有重要意义。其依照真实考试设计,涵盖题型、题量与分值分布,助力考生熟悉考试框架,合理分配答题时间精力,提升效率。同时,模拟题紧扣官方课程,能帮助考生精准定位知识盲点,如大模型架构原理和通义千问应用实践等重点内容,以便针对性完善知识体系。此外,通过练习模拟题,考生可掌握不同题型的解题思维与技巧,快速洞察复杂题目本质,提高答题准确率。

davidkorhenn的博客 3578

SpringBoot整合hdfs,实现文件上传下载删除与批量删除,以及vue前端发送请求,实现前后端交互功能;

spring boot整合hdfs

weixin_45937224的博客 2514

Arcmap计算土方量教程

1. 创建现状TIN 结果数据: 2. 创建设计TIN 结果数据: 注意:现状TIN和设计TIN构建时候的坐标系不同,设置坐标系见最后的注意事项第1点,就是import(导入)那步骤,现状TIN构建时选择高程点数据或者等高线数据,设计TIN构建时候选择洱海面数据 3. TIN转栅格 改成你要存的数据库,注意中间不要有汉字 结果数据(设计TIN转栅格的同理) 4. 裁剪现状TIN转成的栅格 结果数据: 5. 计算土方量 结果数据: ...

欢迎关注,共同进步! 5014

HDFS Java API 与 Spring Boot 3.x 集成:实现文件上传下载REST接口

本文详细介绍了如何将HDFS Java API与Spring Boot 3.x深度集成,构建企业级文件服务的REST接口。通过连接池优化、异常处理、性能监控等生产级策略,实现高效的文件上传下载功能,助力大数据处理与微服务架构的完美融合。

weixin_33146151的博客 249

UE5实战:用Cesium插件打造逼真湖泊效果(附波纹参数调优技巧)

本文详细介绍了在Unreal Engine 5中利用Cesium插件创建逼真区域水效果(如湖泊)的实战流程。重点解析了通过材质层混合机制,精细调整波纹参数(如DistanceScale、NearScale)来模拟不同水体状态,并提供了从资产创建、地形配置到视觉调优的一站式解决方案,帮助开发者高效实现非海洋水域的动态视觉效果。

weixin_29209805的博客 160

uniapp - 微信支付报错 requestPayment:fail:HBuilder mainifest.json中配置的支付appid和生成订单使用的appid不一致,如果是HB调试请在线打包

uni-app,h5移动端网页,微信小程序,支付宝小程序,Android安卓,苹果ios,app,nvue,微信支付,报错,errMsg,requestPayment:fail:HBuildermainifest.json中配置的支付appid和生成订单使用的appid不一致,如果是HB调试请在线打包,http://ask.dcloud.net.cn/article/282,已经都检查了还是报错,如何解决这个问题,还是不行,错误,苹果微信支付报错,安卓app微信支付出错,打包,在线打包也不行,本地打包,支

🏆 前端领域优质创作者 3500

hadoop网盘项目基于hdfs实现文件存储

1.3 温馨提示,如果要使用java客户端的api,本地就必须需要安装hadoop才能调用,如果要脱离环境,可以使用web-api,具体的可以搜索一下hadoop web-api。1.2 windows hadoop util 安装。1.1 Linux hadoop集群搭建。本项目是基于java客户端api实现的。

Honmaple的博客 727

如何在 SAP Server Side JavaScript 里消费 destination

在SAP云平台里打开SAP HANA Web-Based Development Workbench进行服务器端JavaScript的开发。 创建一个新的package: 创建一个新的application: 创建一个新的文件google-distance-api.xshttpdest用于维护destination相关信息: host = "maps.googlea...

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 611

Spring Boot 2.x :通过 spring-boot-starter-hbase 集成 HBase

摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本文内容 HBase 简介和应用场景 spring-boot-starter-hbase 开源简介 集成 HBase 实战 小结 摘录:Many a Man thinks he is buying Pleasure,when he is really...

程序员泥瓦匠 2740

Qt Creator 启动慢、卡顿、无响应 终极解决教程

<think>我们需根据内容生成≤150字的摘要。摘要要涵盖问题、原因、解决方案和效果。注意字数限制。</think>Qt Creator启动慢、卡顿、无响应,主因是配置文件QtCreator.ini膨胀至数十MB(正常<100KB)。删除%APPDATA%\QtProject下的膨胀配置、崩溃备份及qtcreator.db即可,启动从几十秒降至几秒,内存占用恢复正常。辅助优化:关闭有道词典取词、禁用无用插件、调整clangd设置。定期检查配置文件大小,避免多版本共用配置。

Qt历险记,专注Qt技术,三年医疗设备研发经验,1年机器视觉研发经验,半年柔性传感研发经验。 69

YOLOv8改进策略【卷积层】| TIP 2025 MFA 多阶段特征聚合 大核扩感受 + 多梯度融合,强效增强弱小目标特征

红外弱小目标尺寸极小、信噪比低,在特征传递中极易丢失信息;常规卷积感受野单一、梯度源不足,难以同时捕捉局部细节与全局上下文,导致小目标特征表达弱、检测漏检。因此提出多阶段特征聚合 MFA,用大核卷积+多分支+通道注意力强化小目标特征提取。

Limiiiing的博客 58

开发笔记 – Spring Boot集成HBase(Hadoop和Hbase安装)

最近在重新整理搜书吧(一个做图书比价的平台)的系统架构,目前图书产品数量超过了200万条。各种数据加起来超过40G了,使用Mysql数据库存储服务器吃不消,于是考虑使用HBase存储大部分数据。 一、摘要 以前搜书吧的数据量比较小,使用数据库+静态文件存储的方式就可以搞定,主要有2个系统组成:网站前端+后台服务。事先把图书详情等一些固定内容生成html静态文件和前端的其他静态文件打包部署,...

积累与备忘 1645

国家自然基金回避专家有必要填写吗?被回避专家能看到吗?

不填也能中填1–2个最保险只填1个最直接竞争者。✅一句话结论可以不填,但建议填1–2个被回避专家绝对看不到如果你愿意,我可以再告诉你一个很多人不知道的国家自然基金“评审分组规则”——它其实决定了你的项目最可能送到哪些专家手里,这对地区基金中标率影响很大。

qq_15963745的博客 155

Spring Boot 2.x :通过 spring-boot-starter-hbase 集成 HBase ...

本文内容 HBase 简介和应用场景 spring-boot-starter-hbase 开源简介 集成 HBase 实战 小结 一、HBase 简介和应用场景 1.1 HBase 是什么? HBase 是在 Hadoop 分布式文件系统(简称:HDFS)之上的分布式面向列的数据库。而且是 2007 最初原型,历史悠久。那追根究底,Hadoop 是什...

测试0901-1 950

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

java连接hbase_Spring Boot 2.x :通过 spring-boot-starter-hbase 集成 HBase

摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢!本文内容HBase 简介和应用场景spring-boot-starter-hbase 开源简介集成 HBase 实战小结摘录:Many a Man thinks he is buying Pleasure,when he is really sellinghims...

weixin_39633134的博客 253

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

hbase 安装_Spring Boot 通过 spring-boot-starter-hbase 集成 HBase

摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢!本文内容HBase 简介和应用场景spring-boot-starter-hbase 开源简介集成 HBase 实战小结摘录:Many a Man thinks he is buying Pleasure,when he is really sellinghims...

weixin_39684960的博客 269

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

spring boot 支持多少人在线_通过 spring-boot-starter-hbase 集成 HBase

一、HBase 简介和应用场景1.1 HBase 是什么?HBase 是什么?HBase 是在 Hadoop 分布式文件系统(简称:HDFS)之上的分布式面向列的数据库。而且是 2007 最初原型,历史悠久。那追根究底,Hadoop 是什么?Hadoop是一个分布式环境存储并处理大数据。Hadoop 使用 MapReduce 算法统计分析大数据。这时候不得不说下 Google 的著名的三篇大数据的...

weixin_39963853的博客 282
上一篇: Storm1.1.3 HA集群部署
下一篇: AWS 助理架构师认证
大树叶
博客等级 码龄12年 1171粉丝 664原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值