某音热门---图片转字符SpringBoot版


####今天在某音热门视频中刷到的,图片转ASCLL码的视频,抱着试试看的态度我使用Spring Boot做了一个简易web版的。修为尚浅,请多指教!废话少说,上代码。

#环境

/**
*ubuntu
*JDK 1.8
*Spring Boot 1.5.6
*阿里云OSS
*/

#第一步 Maven依赖

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zuoyang</groupId>
    <artifactId>image2ascll</artifactId>
    <version>0.0.2-SNAPSHOT</version>
    <name>image2ascll</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 阿里云OSS-->
        <!-- https://mvnrepository.com/artifact/com.aliyun.oss/aliyun-sdk-oss -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- Thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

#第二步 配置文件

server.port=80

## OSS
#OSS配置
oss.endpoint=你的endPoint(见控制台)
oss.keyid= 你的keyid
oss.keysecre=你的keysecret
oss.bucketname1=你新建的bucket的名字
oss.filehost=你的目录我做测试的是file(就是一个根目录)

#第三步 编写配置文件ConsantConfig.java

package com.zuoyang.image2ascll.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author  左羊
 * @date 2019-07-31 11:53:24
 */
@Component("constantConfig")
public class ConstantConfig {
    @Value("${oss.endpoint}")
    private String LXIMAGE_END_POINT;
    @Value("${oss.keyid}")
    private String LXIMAGE_ACCESS_KEY_ID;
    @Value("${oss.keysecre}")
    private String LXIMAGE_ACCESS_KEY_SECRET;
    @Value("${oss.filehost}")
    private String LXIMAGE_FILE_HOST;
    @Value("${oss.bucketname1}")
    private String LXIMAGE_BUCKET_NAME1;

    public String getLXIMAGE_END_POINT() {
        return LXIMAGE_END_POINT;
    }

    public void setLXIMAGE_END_POINT(String LXIMAGE_END_POINT) {
        this.LXIMAGE_END_POINT = LXIMAGE_END_POINT;
    }

    public String getLXIMAGE_ACCESS_KEY_ID() {
        return LXIMAGE_ACCESS_KEY_ID;
    }

    public void setLXIMAGE_ACCESS_KEY_ID(String LXIMAGE_ACCESS_KEY_ID) {
        this.LXIMAGE_ACCESS_KEY_ID = LXIMAGE_ACCESS_KEY_ID;
    }

    public String getLXIMAGE_ACCESS_KEY_SECRET() {
        return LXIMAGE_ACCESS_KEY_SECRET;
    }

    public void setLXIMAGE_ACCESS_KEY_SECRET(String LXIMAGE_ACCESS_KEY_SECRET) {
        this.LXIMAGE_ACCESS_KEY_SECRET = LXIMAGE_ACCESS_KEY_SECRET;
    }

    public String getLXIMAGE_FILE_HOST() {
        return LXIMAGE_FILE_HOST;
    }

    public void setLXIMAGE_FILE_HOST(String LXIMAGE_FILE_HOST) {
        this.LXIMAGE_FILE_HOST = LXIMAGE_FILE_HOST;
    }

    public String getLXIMAGE_BUCKET_NAME1() {
        return LXIMAGE_BUCKET_NAME1;
    }

    public void setLXIMAGE_BUCKET_NAME1(String LXIMAGE_BUCKET_NAME1) {
        this.LXIMAGE_BUCKET_NAME1 = LXIMAGE_BUCKET_NAME1;
    }
}

#第四步 编写OSS工具类AliyunOSSUtil.java

package com.zuoyang.image2ascll.util;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.zuoyang.image2ascll.config.ConstantConfig;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * @author  左羊
 * @date 2019-07-31 11:53:24
 */
@SuppressWarnings("AliDeprecation")
@Component("aliyunOSSUtil")
public class AliyunOssUtil {

    @Autowired
    private ConstantConfig constantConfig;

    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(AliyunOssUtil.class);

    /**
     * 上传文件
     */
    @SuppressWarnings("AliDeprecation")
    public String upLoad(File file) {
        LOGGER.info("------OSS文件上传开始--------" + file.getName());
        String endpoint = constantConfig.getLXIMAGE_END_POINT();
        System.out.println("获取到的Point为:" + endpoint);
        String accessKeyId = constantConfig.getLXIMAGE_ACCESS_KEY_ID();
        String accessKeySecret = constantConfig.getLXIMAGE_ACCESS_KEY_SECRET();
        String bucketName = constantConfig.getLXIMAGE_BUCKET_NAME1();
        String fileHost = constantConfig.getLXIMAGE_FILE_HOST();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = format.format(new Date());
        String fileGetUrl = null;
        // 判断文件
        if (file == null) {
            return null;
        }
        OSSClient client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        try {
            // 判断容器是否存在,不存在就创建
            if (!client.doesBucketExist(bucketName)) {
                client.createBucket(bucketName);
                CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
                createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
                client.createBucket(createBucketRequest);
            }
            // 设置文件路径和名称
            String fileUrl = fileHost + "/" + (dateStr + "/" + UUID.randomUUID().toString().replace("-", "") + "-" + file.getName());
            System.out.println("A+fileUrl:" + fileUrl);
            // 上传文件
            PutObjectResult result = client.putObject(new PutObjectRequest(bucketName, fileUrl, file));
            // 设置权限(公开读)
            client.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
            if (result != null) {
                LOGGER.info("------OSS文件上传成功------" + fileUrl);
                LOGGER.info("文件上传路径:" + "https://" + constantConfig.getLXIMAGE_BUCKET_NAME1() + "." + constantConfig.getLXIMAGE_END_POINT() + "/" + fileUrl);
                fileGetUrl = "https://" + constantConfig.getLXIMAGE_BUCKET_NAME1() + "." + constantConfig.getLXIMAGE_END_POINT() + "/" + fileUrl;
//                fileGetUrl = "https://" + constantConfig.getLXIMAGE_BUCKET_NAME1() + "." + constantConfig.getLXIMAGE_END_POINT() + "/" + fileUrl;
                return fileGetUrl;
            }
        } catch (OSSException oe) {
            LOGGER.error(oe.getMessage());
        } catch (ClientException ce) {
            LOGGER.error(ce.getErrorMessage());
        } finally {
            if (client != null) {
                client.shutdown();
            }
        }

        return null;
    }

}

#第五步 图片压缩工具类

package com.zuoyang.image2ascll.util;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.*;

/**
 * @author  左羊
 * @date 2019-07-31 11:53:24
 * 图片工具类,完成图片的截取
 * 所有方法返回值均未boolean型
 */
public class ImageHelper {
    /**
     * 实现图像的等比缩放
     * @param source
     * @param targetW
     * @param targetH
     * @return
     */
    private static BufferedImage resize(BufferedImage source, int targetW,
                                        int targetH) {
        // targetW,targetH分别表示目标长和宽
        int type = source.getType();
        BufferedImage target = null;
        double sx = (double) targetW / source.getWidth();
        double sy = (double) targetH / source.getHeight();
        // 这里想实现在targetW,targetH范围内实现等比缩放。如果不需要等比缩放
        // 则将下面的if else语句注释即可
        if (sx < sy) {
            sx = sy;
            targetW = (int) (sx * source.getWidth());
        } else {
            sy = sx;
            targetH = (int) (sy * source.getHeight());
        }
        if (type == BufferedImage.TYPE_CUSTOM) { // handmade
            ColorModel cm = source.getColorModel();
            WritableRaster raster = cm.createCompatibleWritableRaster(targetW,
                    targetH);
            boolean alphaPremultiplied = cm.isAlphaPremultiplied();
            target = new BufferedImage(cm, raster, alphaPremultiplied, null);
        } else
            target = new BufferedImage(targetW, targetH, type);
        Graphics2D g = target.createGraphics();
        // smoother than exlax:
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
        g.dispose();
        return target;
    }

    /**
     * 实现图像的等比缩放和缩放后的截取, 处理成功返回true, 否则返回false
     * @param inFilePath 要截取文件的路径
     * @param outFilePath 截取后输出的路径
     * @param width 要截取宽度
     * @param hight 要截取的高度
     * @throws Exception
     */
    public static boolean compress(String inFilePath, String outFilePath,
                                   int width, int hight) {
        boolean ret = false;
        File file = new File(inFilePath);
        File saveFile = new File(outFilePath);
        InputStream in = null;
        try {
            in = new FileInputStream(file);
            ret = compress(in, saveFile, width, hight);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            ret = false;
        } finally{
            if(null != in){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return ret;
    }

    /**
     * 实现图像的等比缩放和缩放后的截取, 处理成功返回true, 否则返回false
     * @param in 要截取文件流
     * @param width 要截取宽度
     * @param hight 要截取的高度
     * @throws Exception
     */
    public static boolean compress(InputStream in, File saveFile,
                                   int width, int hight) {
//     boolean ret = false;
        BufferedImage srcImage = null;
        try {
            srcImage = ImageIO.read(in);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        if (width > 0 || hight > 0) {
            // 原图的大小
            int sw = srcImage.getWidth();
            int sh = srcImage.getHeight();
            // 如果原图像的大小小于要缩放的图像大小,直接将要缩放的图像复制过去
            if (sw > width && sh > hight) {
                srcImage = resize(srcImage, width, hight);
            } else {
                String fileName = saveFile.getName();
                String formatName = fileName.substring(fileName
                        .lastIndexOf('.') + 1);
                try {
                    ImageIO.write(srcImage, formatName, saveFile);
                } catch (IOException e) {
                    e.printStackTrace();
                    return false;
                }
                return true;
            }
        }
        // 缩放后的图像的宽和高
        int w = srcImage.getWidth();
        int h = srcImage.getHeight();
        // 如果缩放后的图像和要求的图像宽度一样,就对缩放的图像的高度进行截取
        if (w == width) {
            // 计算X轴坐标
            int x = 0;
            int y = h / 2 - hight / 2;
            try {
                saveSubImage(srcImage, new Rectangle(x, y, width, hight), saveFile);
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        // 否则如果是缩放后的图像的高度和要求的图像高度一样,就对缩放后的图像的宽度进行截取
        else if (h == hight) {
            // 计算X轴坐标
            int x = w / 2 - width / 2;
            int y = 0;
            try {
                saveSubImage(srcImage, new Rectangle(x, y, width, hight), saveFile);
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }

        return true;
    }

    /**
     * 实现图像的等比缩放和缩放后的截取, 处理成功返回true, 否则返回false
     * @param in 图片输入流
     * @param saveFile 压缩后的图片输出流
     * @param proportion 压缩比
     * @throws Exception
     */
    public static boolean compress(InputStream in, File saveFile, int proportion) {
        if(null == in
                ||null == saveFile
                ||proportion < 1){// 检查参数有效性
            //LoggerUtil.error(ImageHelper.class, "--invalid parameter, do nothing!");
            return false;
        }

        BufferedImage srcImage = null;
        try {
            srcImage = ImageIO.read(in);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        // 原图的大小
        int width = srcImage.getWidth() / proportion;
        int hight = srcImage.getHeight() / proportion;

        srcImage = resize(srcImage, width, hight);

        // 缩放后的图像的宽和高
        int w = srcImage.getWidth();
        int h = srcImage.getHeight();
        // 如果缩放后的图像和要求的图像宽度一样,就对缩放的图像的高度进行截取
        if (w == width) {
            // 计算X轴坐标
            int x = 0;
            int y = h / 2 - hight / 2;
            try {
                saveSubImage(srcImage, new Rectangle(x, y, width, hight), saveFile);
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        // 否则如果是缩放后的图像的高度和要求的图像高度一样,就对缩放后的图像的宽度进行截取
        else if (h == hight) {
            // 计算X轴坐标
            int x = w / 2 - width / 2;
            int y = 0;
            try {
                saveSubImage(srcImage, new Rectangle(x, y, width, hight), saveFile);
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }

        return true;
    }

    /**
     * 实现缩放后的截图
     * @param image 缩放后的图像
     * @param subImageBounds 要截取的子图的范围
     * @param subImageFile 要保存的文件
     * @throws IOException
     */
    private static void saveSubImage(BufferedImage image,
                                     Rectangle subImageBounds, File subImageFile) throws IOException {
        if (subImageBounds.x < 0 || subImageBounds.y < 0
                || subImageBounds.width - subImageBounds.x > image.getWidth()
                || subImageBounds.height - subImageBounds.y > image.getHeight()) {
            //LoggerUtil.error(ImageHelper.class, "Bad subimage bounds");
            return;
        }
        BufferedImage subImage = image.getSubimage(subImageBounds.x,subImageBounds.y, subImageBounds.width, subImageBounds.height);
        String fileName = subImageFile.getName();
        String formatName = fileName.substring(fileName.lastIndexOf('.') + 1);
        ImageIO.write(subImage, formatName, subImageFile);
    }
}

#第六步 图片转ASCLL工具类

package com.zuoyang.image2ascll.util;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * @author  左羊
 * @date 2019-07-31 11:23:01
 *
 */
public class ImageToAscll {

    /**
     * @param path
     *            图片路径
     */
    public static String createAsciiPic(final String path) {
        final String base = "#8XOHLTI)i=+;:,. ";// 字符串由复杂到简单
        final String txtPath ="/data/image2ascll/txts/abc.txt";
        try {
            final BufferedImage image = ImageIO.read(new File(path));  //读取图片
            //输出到指定文件中
            final BufferedWriter fos = new BufferedWriter(new FileWriter(txtPath,false));   //true表示是否追加
            for (int y = 0; y < image.getHeight(); y += 2) {
                for (int x = 0; x < image.getWidth(); x++) {
                    final int pixel = image.getRGB(x, y);
                    final int r = (pixel & 0xff0000) >> 16, g = (pixel & 0xff00) >> 8, b = pixel & 0xff;
                    final float gray = 0.299f * r + 0.578f * g + 0.114f * b;
                    final int index = Math.round(gray * (base.length() + 1) / 255);
                    String s = index >= base.length() ? " " : String.valueOf(base.charAt(index));
                    fos.write(s);
                }
                fos.newLine();
            }
            fos.close();
        } catch (final IOException e) {
            e.printStackTrace();
        }
        return txtPath;
    }

}

#第七步 Service层
##接口类

package com.zuoyang.image2ascll.service;

/**
 * @author  左羊
 * @date 2019-07-31 11:53:24
 */
public interface ImageToAscllServie {
    String imageToAscll(String path);
}

##实现类

package com.zuoyang.image2ascll.service.imp;

import com.zuoyang.image2ascll.service.ImageToAscllServie;
import com.zuoyang.image2ascll.util.ImageToAscll;
import com.zuoyang.image2ascll.util.ImageHelper;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author  左羊
 * @date 2019-07-31 11:53:24
 */
@Service
public class ImageToAscllServiceImp implements ImageToAscllServie {
    @Override
    public String imageToAscll(String path) {
        InputStream in = null;
        //缩放后需要保存的路径
        File saveFile = new File("/data/image2ascll/txts/ABC.jpg");
        try {
            //原图片的路径
            in = new FileInputStream(new File("/data/image2ascll/txts/"+path));
            if(ImageHelper.compress(in, saveFile, 5)){
                System.out.println("图片压缩5倍!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

       return ImageToAscll.createAsciiPic("/data/image2ascll/txts/ABC.jpg");
    }
}

#第八步 controller层
##IndexController.class

package com.zuoyang.image2ascll.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author  左羊
 * @date 2019-07-31 11:53:24
 */
@Controller
public class IndexController {

    @RequestMapping("/")
    public String index()
    {
        return "imageToAscll";
    }
}

##ImageToAscllController.class

package com.zuoyang.image2ascll.controller;

import com.zuoyang.image2ascll.service.imp.ImageToAscllServiceImp;
import com.zuoyang.image2ascll.util.AliyunOssUtil;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
/**
 * @author  douxiaofeng
 * @date 2019-07-31 11:53:24
 */

@RestController
public class ImageToAscllController {
    private final org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private ImageToAscllServiceImp imageToAscllServiceImp;
    @Autowired
    private AliyunOssUtil aliyunOSSUtil;

    @RequestMapping(value = "/uploadimg")
    @PostMapping
    public @ResponseBody
    String imgUploa(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
        logger.info("文件上传");
        String filename = file.getOriginalFilename();
        logger.info("文件路径 = {}",filename);

        String uploadUrl = null;
        String uploadTxtUrl = null;
        String txtName = null;
        try {
            if (file != null) {
                if (!"".equals(filename.trim())) {
                    // 图片
                    File newFile = new File("/data/image2ascll/txts/"+filename);
                    FileOutputStream os = new FileOutputStream(newFile);
                    os.write(file.getBytes());
                    os.close();
                    file.transferTo(newFile);
                    // 图片上传到OSS
                    uploadUrl = file.getOriginalFilename();
                    logger.info("图片地址={}",uploadUrl);
                    // 文字
                     txtName = imageToAscllServiceImp.imageToAscll(uploadUrl);
                    File newTxtFile = new File(txtName);
                    // 上传到OSS
                    uploadTxtUrl = aliyunOSSUtil.upLoad(newTxtFile);

                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return uploadTxtUrl;
    }
}

#启动类

package com.zuoyang.image2ascll;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author  左羊
 * @date 2019-07-31 11:53:24
 */
@SpringBootApplication
public class Image2ascllApplication {

    public static void main(String[] args) {
        SpringApplication.run(Image2ascllApplication.class, args);
    }

}

#页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>imageToAscll</title>
    <script type="text/javascript" src="lib/jquery-3.2.1.min.js"></script>
    <style>
        #cert{
            /*opacity:0;*/
            /*filter:alpha(opacity=0);*/
            /*position: absolute;*/
            /*top: 0;*/
            /*left: 0;*/
            z-index: 9;
            display: none;
        }
    </style>
</head>
<body>
<button type="button" id="insert_image_button">添加图片
    <input id="cert" type="file"/>
</button>


</body>
<script>
    $(function () {
        $("#download-box").hide();
    // 添加图片
    $("#insert_image_button").click(function () {
        document.getElementById("cert").click();
    });

    //    图片上传
    function upload() {
        var type = "file";          //后台接收时需要的参数名称,自定义即可
        var id = "cert";            //即input的id,用来寻找值
        var formData = new FormData();
        formData.append(type, $("#" + id)[0].files[0]);    //生成一对表单属性
        $.ajax({
            type: "POST",           //因为是传输文件,所以必须是post
            url: '/uploadimg',         //对应的后台处理类的地址
            data: formData,
            processData: false,
            contentType: false,
            success: function (data) {
                window.open(data);
                // location.href=data;
            }
        });
    };

    //图片上传引用
    $("#cert").on("change", function () {
        if ($("#cert").val() != "") {
            upload();
        }
    });



    });
</script>
</html>

#测试 生成jar文件

#上传至服务器,启动jar文件 --》 浏览器输入地址

##看下结果

将显示文字复制到记事本

#最后附上博主大饼脸



###参考地址:
https://blog.csdn.net/abubu123/article/details/77099950
https://blog.csdn.net/renhd_1987/article/details/52948978
###github地址:
https://github.com/dxf1998/imageToAscll.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值