jeecg-boot中上传图片到华为云obs云存储中

大家好,我是雄雄,欢迎关注微信公众号:雄雄的小课堂。

在这里插入图片描述

前言

jeecg-boot框架中,其实对接的功能还是挺多的,其中就有文件云存储服务器,不过是阿里云oss的,那如果我们使用的是七牛云,或者华为云obs呢?我们需要改哪些内容才能实现上传obs的目的呢,今天我们就来看看如何使用jeecg-boot来实现将文件(图片)上传到华为云obs中。

配置nacos

因为我们用的是微服务版本,所以配置都在nacos中,如果是普通的boot版本的话,直接在yml文件中写配置就行。
nacos的配置如下:

  1. 找到jeecg节点,修改uploadType的值:
uploadType: hwobs

记住这个hwobs的值,我们需要在代码中声明一下它,相当于一个变量,这个值必须和代码中声明的那个变量的值一样,不然就找不到了。

  1. 添加obs节点:
  #华为云oss存储配置
  obs:
    endpoint: obs.cn-xxxxx-9.myhuaweicloud.com
    accessKey: KD6BYxxxxxx
    secretKey: Zv83xxxxxx
    bucketName: xxxxx
    staticDomain: xxxxx.obs.cn-xxxxxx-9.myhuaweicloud.com

注意:对应的值需要改成你自己的ak、sk等。

修改POM文件,添加华为云obs的依赖

添加代码如下:

<!--华为云OBS-->
		<dependency>
			<groupId>com.huaweicloud</groupId>
			<artifactId>esdk-obs-java</artifactId>
			<version>3.21.8</version>
			<exclusions>
				<exclusion>
					<groupId>com.squareup.okhttp3</groupId>
					<artifactId>okhttp</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

新建OBS工具类ObsBootUtil

package org.jeecg.common.util.oss;

import com.obs.services.ObsClient;
import com.obs.services.model.ObsObject;
import com.obs.services.model.PutObjectResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.FileItemStream;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.filter.FileTypeFilter;
import org.jeecg.common.util.filter.StrAttackFilter;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.UUID;

/**
 * @Description: 华为云 oss 上传工具类(高依赖版)
 * @Date: 2019/5/10
 * @author: jeecg-boot
 */
@Slf4j
public class ObsBootUtil {

    private static String endPoint;
    private static String accessKeyId;
    private static String accessKeySecret;
    private static String bucketName;
    /**
     * oss 工具客户端
     */
    private static ObsClient ossClient = null;

    public static String getEndPoint() {
        return endPoint;
    }

    public static void setEndPoint(String endPoint) {
        ObsBootUtil.endPoint = endPoint;
    }

    public static String getAccessKeyId() {
        return accessKeyId;
    }

    public static void setAccessKeyId(String accessKeyId) {
        ObsBootUtil.accessKeyId = accessKeyId;
    }

    public static String getAccessKeySecret() {
        return accessKeySecret;
    }

    public static void setAccessKeySecret(String accessKeySecret) {
        ObsBootUtil.accessKeySecret = accessKeySecret;
    }

    public static String getBucketName() {
        return bucketName;
    }

    public static void setBucketName(String bucketName) {
        ObsBootUtil.bucketName = bucketName;
    }

    public static ObsClient getOssClient() {
        return ossClient;
    }

    /**
     * 上传文件至华为云 OBS
     * 文件上传成功,返回文件完整访问路径
     * 文件上传失败,返回 null
     *
     * @param file    待上传文件
     * @param fileDir 文件保存目录
     * @return oss 中的相对文件路径
     */
    public static String upload(MultipartFile file, String fileDir, String customBucket) throws Exception {
        //update-begin-author:liusq date:20210809 for: 过滤上传文件类型
        FileTypeFilter.fileTypeFilter(file);
        //update-end-author:liusq date:20210809 for: 过滤上传文件类型

        String filePath;
        initOss(endPoint, accessKeyId, accessKeySecret);
        StringBuilder fileUrl = new StringBuilder();
        String newBucket = bucketName;
        if (oConvertUtils.isNotEmpty(customBucket)) {
            newBucket = customBucket;
        }
        try {
            //判断桶是否存在,不存在则创建桶
            if (!ossClient.headBucket(newBucket)) {
                ossClient.createBucket(newBucket);
            }
            // 获取文件名
            String orgName = file.getOriginalFilename();
            if ("".equals(orgName) || orgName == null) {
                orgName = file.getName();
            }
            orgName = CommonUtils.getFileName(orgName);
            String fileName = !orgName.contains(".")
                    ? orgName + "_" + System.currentTimeMillis()
                    : orgName.substring(0, orgName.lastIndexOf("."))
                    + "_" + System.currentTimeMillis()
                    + orgName.substring(orgName.lastIndexOf("."));
            if (!fileDir.endsWith(SymbolConstant.SINGLE_SLASH)) {
                fileDir = fileDir.concat(SymbolConstant.SINGLE_SLASH);
            }
            //update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
            fileDir = StrAttackFilter.filter(fileDir);
            //update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
            fileUrl.append(fileDir).append(fileName);

            filePath = "https://" + newBucket + "." + endPoint + SymbolConstant.SINGLE_SLASH + fileUrl;

            PutObjectResult result = ossClient.putObject(newBucket, fileUrl.toString(), file.getInputStream());
            // 设置权限(公开读)
//            ossClient.setBucketAcl(newBucket, CannedAccessControlList.PublicRead);
            if (result != null) {
                log.info("------OSS文件上传成功------" + fileUrl);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return filePath;
    }

    /**
     * 文件上传
     *
     * @param file    文件
     * @param fileDir fileDir
     * @return 路径
     */
    public static String upload(MultipartFile file, String fileDir) throws Exception {
        return upload(file, fileDir, null);
    }

    /**
     * 上传文件至华为云 OBS
     * 文件上传成功,返回文件完整访问路径
     * 文件上传失败,返回 null
     *
     * @param file    待上传文件
     * @param fileDir 文件保存目录
     * @return oss 中的相对文件路径
     */
    public static String upload(FileItemStream file, String fileDir) {
        String filePath;
        initOss(endPoint, accessKeyId, accessKeySecret);
        StringBuilder fileUrl = new StringBuilder();
        try {
            String suffix = file.getName().substring(file.getName().lastIndexOf('.'));
            String fileName = UUID.randomUUID().toString().replace("-", "") + suffix;
            if (!fileDir.endsWith(SymbolConstant.SINGLE_SLASH)) {
                fileDir = fileDir.concat(SymbolConstant.SINGLE_SLASH);
            }
            fileDir = StrAttackFilter.filter(fileDir);
            fileUrl.append(fileDir).append(fileName);

            filePath = "https://" + bucketName + "." + endPoint + SymbolConstant.SINGLE_SLASH + fileUrl;

            PutObjectResult result = ossClient.putObject(bucketName, fileUrl.toString(), file.openStream());
            // 设置权限(公开读)
            //ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
            if (result != null) {
                log.info("------OSS文件上传成功------" + fileUrl);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return filePath;
    }

    /**
     * 删除文件
     *
     * @param url 路径
     */
    public static void deleteUrl(String url) {
        deleteUrl(url, null);
    }

    /**
     * 删除文件
     *
     * @param url 路径
     */
    public static void deleteUrl(String url, String bucket) {
        String newBucket = bucketName;
        if (oConvertUtils.isNotEmpty(bucket)) {
            newBucket = bucket;
        }
        String bucketUrl = "https://" + newBucket + "." + endPoint + SymbolConstant.SINGLE_SLASH;

        //TODO 暂时不允许删除云存储的文件
        //initOss(endPoint, accessKeyId, accessKeySecret);
        url = url.replace(bucketUrl, "");
        ossClient.deleteObject(newBucket, url);
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名称
     */
    public static void delete(String fileName) {
        ossClient.deleteObject(bucketName, fileName);
    }

    /**
     * 获取文件流
     *
     * @param objectName 对象名
     * @param bucket     桶
     * @return 文件流
     */
    public static InputStream getOssFile(String objectName, String bucket) {
        InputStream inputStream = null;
        try {
            String newBucket = bucketName;
            if (oConvertUtils.isNotEmpty(bucket)) {
                newBucket = bucket;
            }
            initOss(endPoint, accessKeyId, accessKeySecret);
            //update-begin---author:liusq  Date:20220120  for:替换objectName前缀,防止key不一致导致获取不到文件----
            objectName = ObsBootUtil.replacePrefix(objectName, bucket);
            //update-end---author:liusq  Date:20220120  for:替换objectName前缀,防止key不一致导致获取不到文件----
            ObsObject ossObject = ossClient.getObject(newBucket, objectName);
            inputStream = new BufferedInputStream(ossObject.getObjectContent());
        } catch (Exception e) {
            log.info("文件获取失败" + e.getMessage());
        }
        return inputStream;
    }

    /**
     * 获取文件外链
     *
     * @param bucketName 桶名称
     * @param objectName 对项名
     * @param expires    日期
     * @return 外链
     */
    public static String getObjectUrl(String bucketName, String objectName, Date expires) {
        initOss(endPoint, accessKeyId, accessKeySecret);
        try {
            //update-begin---author:liusq  Date:20220120  for:替换objectName前缀,防止key不一致导致获取不到文件----
            objectName = ObsBootUtil.replacePrefix(objectName, bucketName);
            //update-end---author:liusq  Date:20220120  for:替换objectName前缀,防止key不一致导致获取不到文件----
            if (ossClient.doesObjectExist(bucketName, objectName)) {
                //URL url = ossClient.generatePresignedUrl(bucketName, objectName, expires);
                //log.info("原始url : {}", url.toString());
                //log.info("decode url : {}", URLDecoder.decode(url.toString(), "UTF-8"));
                //【issues/4023】问题 oss外链经过转编码后,部分无效,大概在三分一;无需转编码直接返回即可 #4023
                //return url.toString();
                return "";
            }
        } catch (Exception e) {
            log.info("文件路径获取失败" + e.getMessage());
        }
        return null;
    }

    /**
     * 初始化 oss 客户端
     */
    private static void initOss(String endpoint, String accessKeyId, String accessKeySecret) {
        if (ossClient == null) {
            ossClient = new ObsClient(accessKeyId, accessKeySecret, endpoint);
        }
    }


    /**
     * 上传文件到oss
     *
     * @param stream       文件流
     * @param relativePath 相对路径
     * @return 文件路径
     */
    public static String upload(InputStream stream, String relativePath) {
        String filePath = "https://" + bucketName + "." + endPoint + SymbolConstant.SINGLE_SLASH + relativePath;
        initOss(endPoint, accessKeyId, accessKeySecret);
        PutObjectResult result = ossClient.putObject(bucketName, relativePath, stream);
        // 设置权限(公开读)
        //ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
        if (result != null) {
            log.info("------OSS文件上传成功------" + relativePath);
        }
        return filePath;
    }

    /**
     * 替换前缀,防止key不一致导致获取不到文件
     *
     * @param objectName   文件上传路径 key
     * @param customBucket 自定义桶
     * @return 对象名
     * @date 2022-01-20
     * @author lsq
     */
    private static String replacePrefix(String objectName, String customBucket) {
        log.info("------replacePrefix---替换前---objectName:{}", objectName);

        String newBucket = bucketName;
        if (oConvertUtils.isNotEmpty(customBucket)) {
            newBucket = customBucket;
        }
        String path = "https://" + newBucket + "." + endPoint + SymbolConstant.SINGLE_SLASH;

        objectName = objectName.replace(path, "");

        log.info("------replacePrefix---替换后---objectName:{}", objectName);
        return objectName;
    }

    public static String getOriginalUrl(String url) {
        return url;
    }

}

新建OBS配置类

代码如下:


package org.jeecg.config.oss;

import org.jeecg.common.util.oss.ObsBootUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 华为云存储 配置
 *
 * @author: jeecg-boot
 */
@Configuration
public class ObsConfig {

    @Value("${jeecg.obs.endpoint}")
    private String endpoint;
    @Value("${jeecg.obs.accessKey}")
    private String accessKeyId;
    @Value("${jeecg.obs.secretKey}")
    private String accessKeySecret;
    @Value("${jeecg.obs.bucketName}")
    private String bucketName;


    @Bean
    public void initObsBootConfig() {
        ObsBootUtil.setEndPoint(endpoint);
        ObsBootUtil.setAccessKeyId(accessKeyId);
        ObsBootUtil.setAccessKeySecret(accessKeySecret);
        ObsBootUtil.setBucketName(bucketName);
    }
}

声明配置变量

CommonConstant接口中声明在nacos中配置的变量:

String UPLOAD_TYPE_OBS = "hwobs";

在这里插入图片描述

修改原来文件上传的方法

首先找到类CommonUtils,的uploadOnlineImage方法,修改成如下:
在这里插入图片描述

public static String uploadOnlineImage(byte[] data, String basePath, String bizPath, String uploadType) {
        String dbPath = null;
        String fileName = "image" + Math.round(Math.random() * 100000000000L);
        fileName += "." + PoiPublicUtil.getFileExtendName(data);
        try {
            if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
                File file = new File(basePath + File.separator + bizPath + File.separator);
                if (!file.exists()) {
                    file.mkdirs();// 创建文件根目录
                }
                String savePath = file.getPath() + File.separator + fileName;
                File savefile = new File(savePath);
                FileCopyUtils.copy(data, savefile);
                dbPath = bizPath + File.separator + fileName;
            } else {
                InputStream in = new ByteArrayInputStream(data);
                String relativePath = bizPath + "/" + fileName;
                if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
                    dbPath = MinioUtil.upload(in, relativePath);
                } else if (CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)) {
                    dbPath = OssBootUtil.upload(in, relativePath);
                } else {
                    dbPath = ObsBootUtil.upload(in, relativePath);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dbPath;
    }

修改upload方法:
在这里插入图片描述

 /**
     * 统一全局上传
     *
     * @Return: java.lang.String
     */
    public static String upload(MultipartFile file, String bizPath, String uploadType) {
        String url = "";
        try {
            if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
                url = MinioUtil.upload(file, bizPath);
            }  else if (CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)) {
                url = OssBootUtil.upload(file, bizPath);
            }else{
                url = ObsBootUtil.upload(file, bizPath);
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return url;
    }

修改OssFileServiceImpl实现类的upload方法(当然你新写一个也行):

在这里插入图片描述

/**
 * @Description: OSS云存储实现类
 * @author: jeecg-boot
 */
@Service("ossFileService")
public class OssFileServiceImpl extends ServiceImpl<OssFileMapper, OssFile> implements IOssFileService {

	@Override
	public void upload(MultipartFile multipartFile) throws Exception {
		String fileName = multipartFile.getOriginalFilename();
		fileName = CommonUtils.getFileName(fileName);
		OssFile ossFile = new OssFile();
		ossFile.setFileName(fileName);
		//String url = OssBootUtil.upload(multipartFile,"upload/test");
		//改成华为云的
		String url = ObsBootUtil.upload(multipartFile,"upload/test");
		//update-begin--Author:scott  Date:20201227 for:JT-361【文件预览】阿里云原生域名可以文件预览,自己映射域名kkfileview提示文件下载失败-------------------
		// 返回阿里云原生域名前缀URL
		//ossFile.setUrl(OssBootUtil.getOriginalUrl(url));

		// 返回华为云原生域名前缀URL
		ossFile.setUrl(ObsBootUtil.getOriginalUrl(url));
		//update-end--Author:scott  Date:20201227 for:JT-361【文件预览】阿里云原生域名可以文件预览,自己映射域名kkfileview提示文件下载失败-------------------
		this.save(ossFile);
	}

	@Override
	public boolean delete(OssFile ossFile) {
		try {
			this.removeById(ossFile.getId());
			//OssBootUtil.deleteUrl(ossFile.getUrl());
			ObsBootUtil.deleteUrl(ossFile.getUrl());
		}
		catch (Exception ex) {
			log.error(ex.getMessage(),ex);
			return false;
		}
		return true;
	}

}

然后就大功告成了!!!

<think>我们正在解决的是用户在使用华为云Ascend仓库时遇到的SSL证书验证错误: 错误信息:ERROR: cannot verify ascend-repo.obs.cn-east-2.myhuaweicloud.com's certificate Self-signed certificate encountered. 根据引用中提到的解决证书验证问题的方法,我们可以采用类似的思路: 引用[1]中提到,当连接到mirrors.tuna.tsinghua.edu.cn不安全时,使用wget加上`--no-check-certificate`参数跳过证书检查。 引用[2]和引用[3]则通过修改docker的daemon.json配置文件来更换镜像源,但这里的问题不是镜像源,而是证书问题。 然而,用户当前的错误是遇到了自签名证书(Self-signed certificate),这意味着该站点的证书不是由受信任的证书颁发机构(CA)签发的,因此被系统视为不安全。 解决方案通常有两种思路: 1. 跳过证书验证(不推荐,因为存在安全风险,但简单直接) 2. 将自签名证书添加到系统的受信任证书中(推荐,但步骤稍多) 具体步骤: 方法一:跳过证书验证(快速但不安全) 在访问该仓库的命令中(如wget、curl、apt等)添加跳过证书验证的选项。 例如: - 使用wget:添加`--no-check-certificate`选项 - 使用curl:添加`-k`或`--insecure`选项 - 对于apt(如果apt源使用该仓库):不建议全局跳过,但可以临时修改配置文件(不安全,不推荐) 但是,用户的具体命令没有给出,所以需要根据实际命令来添加参数。 方法二:将自签名证书添加到受信任根证书(安全,推荐) 步骤: 1. 获取自签名证书 我们可以使用openssl命令从目标服务器获取证书: ```bash openssl s_client -connect ascend-repo.obs.cn-east-2.myhuaweicloud.com:443 -showcerts </dev/null 2>/dev/null|openssl x509 -outform PEM > huawei_ascend.crt ``` 或者,如果无法获取,可以联系华为云技术支持提供该证书。 2. 将证书复制到系统证书目录 ```bash sudo cp huawei_ascend.crt /usr/local/share/ca-certificates/ ``` 3. 更新证书存储 ```bash sudo update-ca-certificates ``` 4. 验证:再次执行访问该仓库的命令,应该不会出现证书错误。 方法三:临时设置环境变量(针对特定命令) 对于Python的pip,可以设置环境变量: ```bash export PIP_OPTIONS="--trusted-host ascend-repo.obs.cn-east-2.myhuaweicloud.com" ``` 然后使用pip安装时就会跳过对该主机的证书验证。 注意:方法二需要管理员权限,且对系统全局有效。方法一和方法三只对当前命令有效。 根据用户的具体情况,如果是在使用apt-get更新或安装软件时遇到此错误,那么需要修改apt的配置文件: 在/etc/apt/sources.list.d/目录下找到对应的源文件,然后在源地址前添加`[trusted=yes]`,例如: 将原本的: deb https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/... ... 改为: deb [trusted=yes] https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/... ... 然后执行`sudo apt-get update`。 但是,请注意,这样会跳过对该源的证书验证,同样存在安全风险。 总结:推荐使用方法二,将证书添加到系统信任证书中,这样既安全又不会影响其他操作。 相关问题: 1. 如何查看Linux系统已安装的受信任证书? 2. 如何为curl命令永久跳过特定主机的证书验证? 3. 在Ubuntu中如何移除已添加的自签名证书?</think>### 解决华为云 Ascend 仓库 SSL 证书验证错误 当遇到 `ERROR: cannot verify ascend-repo.obs.cn-east-2.myhuaweicloud.com's certificate Self-signed certificate encountered` 错误时,可通过以下方法解决: --- #### 方法一:跳过证书验证(临时方案) 在命令中添加 `--no-check-certificate` 参数: ```bash wget https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/... --no-check-certificate ``` 或 ```bash curl -k https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/... ``` > **适用场景**:快速测试或非敏感操作 > **风险**:跳过安全验证,存在中间人攻击风险[^1] --- #### 方法二:添加证书到信任列表(推荐方案) 1. **下载证书**: ```bash openssl s_client -connect ascend-repo.obs.cn-east-2.myhuaweicloud.com:443 -showcerts </dev/null 2>/dev/null|openssl x509 -outform PEM > huawei_ascend.crt ``` 2. **安装证书**: ```bash sudo cp huawei_ascend.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates ``` 3. **验证安装**: ```bash curl -v https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/ ``` > 观察输出中应出现 `SSL certificate verify ok` --- #### 方法三:配置系统级信任(长期方案) 修改全局 SSL 配置: ```bash sudo nano /etc/ssl/openssl.cnf ``` 在 `[v3_ca]` 部分添加: ```ini subjectAltName = DNS:ascend-repo.obs.cn-east-2.myhuaweicloud.com ``` --- #### 方法四:针对特定工具配置 **APT 包管理器**: ```bash sudo nano /etc/apt/apt.conf.d/99verify-ascend ``` 添加: ```conf Acquire::https::ascend-repo.obs.cn-east-2.myhuaweicloud.com::Verify-Peer "false"; Acquire::https::ascend-repo.obs.cn-east-2.myhuaweicloud.com::Verify-Host "false"; ``` --- ### 原理说明 此错误通常由以下原因引起: 1. **自签名证书**:服务器使用未通过公共 CA 认证的证书 2. **证书链不完整**:缺少中间证书 3. **域名不匹配**:证书中的 SAN (Subject Alternative Name) 未包含访问域名[^3] > **建议**:优先使用方法二,既保持安全性又解决验证问题。如为生产环境,建议联系华为云获取官方 CA 签名的证书。 --- ### 相关问题 1. 如何验证 SSL 证书的有效期和签发机构? 2. 在 Docker 中如何解决类似的证书验证错误? 3. 自签名证书和 CA 签名证书有哪些安全差异? 4. 如何为 Nginx 配置自定义 SSL 证书?[^2] [^1]: 跳过证书验证仅适用于测试环境,生产环境存在安全风险 [^2]: 修改系统级配置需谨慎操作,建议备份原始文件 [^3]: SAN 扩展确保证书支持多域名验证,是 HTTPS 安全的关键组件
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穆雄雄

哎,貌似还没开张来着呢~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值