FTP部署及使用

基本原理:


用户使用支持FTP协议的客户端,连接到在远程主机上的FTP服务端程。用户通过客户端向服务端发出命令,服务端就会执行用户所发出的命令,然后将执行的结果返回到客户端。
端口:
通常默认使用TCP端口中的 20和21这两个端口
20传输数据,21传输控制信息

1.安装vsftpd

yum -y install vsftpd

2.关闭匿名访问

        可以不关,不关就可以不输入用户名密码直接访问

vim /etc/vsftpd/vsftpd.conf

 3.启动服务

systemctl start vsftpd.service

重启

systemctl restart vsftpd.service

4.查看服务状态

systemctl status vsftpd.service

 5.关闭防火墙

systemctl disable firewalld 

新建一个用户,用于操作FTP。并将文件保存在用户目录下

adduser  ftpuser   #创建账号
passwd   ftpuser   #设定密码

 6.设置主目录

创建目录

mkdir -p /data/ftp

设置访问权限

chmod a-w /data/ftp && chmod 777 -R /data/ftp

 设为用户的主目录

usermod -d /data/ftp ftpuser

通过本地文件夹  ftp://ip 进行访问 

 

java实现链接ftp 

添加对应的依赖

<!--        ftp上传-->
        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.8.0</version>
        </dependency>

 一:配置连接FTP静态方法,或者定义一个util

 /**
     * @param url      FTP服务器hostname
     * @param port     FTP服务器端口
     * @param username FTP登录账号
     * @param password FTP登录密码
     * @param path     FTP服务器保存目录
     * @param filename 文件名 6403_APP_YYYYMMDD_渠道标志_批次号.txt
     * @param inputStream    输入流
     * @return
     */
    private static boolean uploadFileFTP(String url, int port, String username, String password,
                                  String path, String filename, InputStream inputStream) {


        boolean success;
        FTPClient ftp = new FTPClient();
        try {
            int reply;
            // 链接 FTP 服务器
            // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
            ftp.setConnectTimeout(10000*10000);
            ftp.connect(url);
            // 登录
            ftp.login(username, password);
            // 返回链接码
            reply = ftp.getReplyCode();
            // 设置文件格式为二进制类型
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
                log.info("FTP服务器 拒绝连接");
                return false;
            }
            ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftp.makeDirectory(path);
            // 跳转到指定的目录路径
            ftp.changeWorkingDirectory(path);
            ftp.enterLocalPassiveMode();
            // 存储文件
            ftp.storeFile(filename, inputStream);
            inputStream.close();
            ftp.logout();
            success = true;
        } catch (IOException e) {
            log.error("FTP服务器 文件上传失败 失败原因:{}", e.getMessage(), e);
            return false;
        } finally {
            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException e) {
                    log.error("FTP服务器 关闭失败 失败原因:{}", e.getMessage(), e);
                }
            }
        }
        return success;
    }

二:创建接口进行上传图片

    @RequestMapping("/addUser")
    public Object addUser(@RequestParam("headPic")MultipartFile file) throws IOException {

        InputStream inputStream = file.getInputStream(); //获取上传的文件流
        String filename = file.getOriginalFilename();//获取上传的文件名
        String suffix = filename.substring(filename.lastIndexOf("."));//获取后缀
        String fileName = UUID.randomUUID().toString()+suffix; //定义一个不重复的名字
        boolean b = uploadFileFTP("0.0.0.0", 21, "ftpuser", "ftpuser", "/root/tomcat/apache-tomcat-8.5.81/webapps/yuansheng", fileName, inputStream);
        return b;
    }

使用postman进行测试:

上传文件进行测试

 一般都会存在用户目录下:

如果不是用户的目录下地址,则自动会存入用户下。(只会跑到用户目录下存储)

获取存入的图片或者文件


@RestController
public class FtpController {

    @RequestMapping("/getImage")
    @ResponseBody
    public void getEventPicture(HttpServletRequest request, HttpServletResponse response) {
        String imgPath = "a33e9bcc5137f7b49b8fbd5c9e1b3bb.png"; //传入图片名字
        FTPClient ftp=null;
        InputStream in = null;
        OutputStream os = null;
        try {
            ftp= initFTP(ftp);
            //下载文件 FTP协议里面,规定文件名编码为iso-8859-1,所以读取时要将文件名转码为iso-8859-1
            //如果没有设置按照UTF-8读,获取的流是空值null
            in = ftp.retrieveFileStream(new String(imgPath.getBytes("UTF-8"), "iso-8859-1"));
            String picType = imgPath.split("\\.")[1];
            BufferedImage bufImg = null;
            bufImg = ImageIO.read(in);
            os = response.getOutputStream();
            ImageIO.write(bufImg, picType, os);

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(in!=null) {
                try {
                    in.close();
                    destroy(ftp);
                } catch (IOException e) {
                }
            }
        }
    }

    //通过读取ftp.properties文件来初始化ftp的参数
    private FTPClient initFTP(FTPClient ftp) throws IOException {

        String addr="0.0.0.0";  //FTP服务器hostname
        int port =21;                //FTP服务器端口
        String username ="ftpuser";   //FTP登录账号
        String password ="ftpuser";   //FTP登录密码
        String path="/home/ftpuser/files";              //FTP服务器保存目录

        ftp = new FTPClient();
        ftp.connect(addr,port);
        ftp.login(username,password);
        ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
        ftp.setControlEncoding("GBK");
        ftp.setBufferSize(1024*1024*10); //设置缓冲区上传速度为10M,默认为1K
        ftp.setFileType(FTP.BINARY_FILE_TYPE);//设置上传方式位字节
        ftp.enterLocalPassiveMode();//Switch to passive mode
        return ftp;
    }

    //关闭FTP客户端
    private void destroy(FTPClient ftp) throws IOException {
        if(ftp != null){
            ftp.disconnect();
            ftp = null;
        }
    }

}

前端js代码

//中文在ie浏览器上需要encodeURI()重新编码防止参数错误
var path="testimg.jpg";
var img="<image height='500px' width='600px' src='"+
"url/getImage.do?imgPath="+encodeURI(path)+"alt="+path+"/>";
//将图片添加到指定div
$('#divId').append(img);

postman测试

ftp删除图片 

 //ftp服务器ip地址
    private static final String FTP_ADDRESS = "0.0.00.0";
    //端口号
    private static final int FTP_PORT = 21;
    //用户名
    private static final String FTP_USERNAME = "ftpuser";
    //密码
    private static final String FTP_PASSWORD = "ftpuser";
    //图片路径
    public static final String FTP_BASEPATH = "/www/wwwroot/ftpuser";


    public static boolean deleteFile(String FileName) {
        boolean success = false;
        FTPClient ftp = new FTPClient();
        ftp.setControlEncoding("GBK");
        try {
            int reply;
            ftp.connect(FTP_ADDRESS, FTP_PORT);// 连接FTP服务器
            ftp.login(FTP_USERNAME, FTP_PASSWORD);// 登录
            reply = ftp.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
                return success;
            }
            ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftp.makeDirectory(FTP_BASEPATH);
            ftp.changeWorkingDirectory(FTP_BASEPATH);
            success = ftp.deleteFile(FileName);
            ftp.logout();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException ioe) {
                }
            }
        }
        return success;
    }

FTP获取音频文件

 /**
     * 获取影音文件  (response-header设置不同)
     * @param response 。
     * @param remote 文件名字
     * @return void
     **/
    @RequestMapping(value = "/getImages",method = RequestMethod.GET)
    @ResponseBody
    @ApiOperation("获取音频文件")
    public void downloadAudio(HttpServletResponse response, String remote) throws IOException {
//        initFtpClient();
//        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
//        String fileName = remote.substring(remote.lastIndexOf(FILE_SEPARATOR) + 1);
//        InputStream ins = ftpClient.retrieveFileStream(remote);

        System.out.println("remote = " + remote);
        FTPClient ftp=null;
        InputStream in = null;
        OutputStream os = null;
        try {
            ftp = initFTP(ftp);
            //下载文件 FTP协议里面,规定文件名编码为iso-8859-1,所以读取时要将文件名转码为iso-8859-1
            //如果没有设置按照UTF-8读,获取的流是空值null
            in = ftp.retrieveFileStream(remote);
        }catch (Exception e){

        }
        if (null == in) {
            return;
        }
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        byte[] buf = new byte[204800];
        int bufsize = 0;
        while ((bufsize = in.read(buf, 0, buf.length)) != -1) {
            byteOut.write(buf, 0, bufsize);
        }
        byte[] return_arraybyte = byteOut.toByteArray();
        response.reset();
        //设置响应头
        response.addHeader("Content-Disposition", "attachment;filename=" + new String(remote.getBytes("gb2312"), "ISO8859-1"));
        response.setContentType("application/octet-stream");
        response.addHeader("Accept-Ranges","bytes");
        int length = return_arraybyte.length;
        response.setContentLength(length);
        byteOut.close();
        in.close();
        ServletOutputStream outputStream = response.getOutputStream();
        BufferedOutputStream toClient = new BufferedOutputStream(outputStream);
        toClient.write(return_arraybyte);
        toClient.flush();
        outputStream.close();
        toClient.close();
    }

前端使用

<audio src="地址.mp3" controls="controls"></audio>  标签进行使用

 

Ftp获取视频

 @GetMapping("/getRmove")
    @ApiOperation("获取视频文件")
    public void getRmove(HttpServletResponse response,String remote) {
        //创建FTPClient
        FTPClient ftp = new FTPClient();
        ftp.setControlEncoding("GBK");
        try {
            int reply;
            ftp.connect("你的端口号", 21);// 连接FTP服务器
            // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
            ftp.login("ftpxxxx", "ftpxxxx");// 登录
            reply = ftp.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftp.disconnect();
            }
            ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftp.changeWorkingDirectory("/www/wwwroot/ftpuser");//切换到文件保存目录
            ftp.enterLocalPassiveMode();
            FTPFile[] ftpFiles = ftp.listFiles();//获取FTP服务器上的文件别表
            InputStream in = null;
            for (FTPFile file : ftpFiles) {  //遍历文件列表
                // 取得指定文件
                if (file.getName().equals(remote)) {
                    if (file.isFile()) {
                        String fileName = file.getName();
                        //获取文件流
                        in = ftp.retrieveFileStream(new String(fileName.getBytes("gbk"), "ISO-8859-1"));
                        //创建ByteArrayOutputStream
                        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
                        int ch;
                        //循环将文件流写入ByteArrayOutputStream 中
                        while ((ch = in.read()) != -1) {
                            swapStream.write(ch);
                        }
                        //将ByteArrayOutputStream 转成byte[]
                        byte[] bytes = swapStream.toByteArray();
                        //通过输出流输出即可
                        response.addHeader("Access-Contro1-A11ow-0rigin", "*");
                        response.getOutputStream().write(bytes);
                    }
                }
            }
            in.close();

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

阿里OSS上传预览

阿里oss控制台:OSS管理控制台 (aliyun.com)

阿里ossAPI文档:Java (aliyun.com)

依赖

     <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.15.0</version>
        </dependency>

上传:

package com.ruoyi.web.jxxt.controller;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.*;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

@Api(tags = "阿里云上传下载")
@RestController
@RequestMapping("/jx/oss")
public class AliyunOSSController {

    @PostMapping("/png")
    public List<String> png(@RequestBody MultipartFile[] files){

        // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // 阿里云账号access Key和access Id
        String accessKeyId = "LTAI5txxxxx";
        String accessKeySecret = "Gc4EFvfAMxxxxx";
        // 填写Bucket名称,桶的名称
        String bucketName = "maixxxxx";

//        ObjectMetadata meta = new ObjectMetadata();
//        meta.setContentType("image/jpg");
        ArrayList<String> strings = new ArrayList<>();

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            for (MultipartFile file:files){
                // 文件名
                String originalFilename = file.getOriginalFilename();
                // 新的文件名 = 存储桶名称_时间戳.后缀名
                String fileName = bucketName + "_" + System.currentTimeMillis() + originalFilename.substring(originalFilename.lastIndexOf("."));
                System.out.println("fileName = " + fileName);
                // 创建PutObjectRequest对象。
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, file.getInputStream());
                // 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
                ObjectMetadata metadata = new ObjectMetadata();
                metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
                metadata.setObjectAcl(CannedAccessControlList.Private);
                metadata.setContentType("image/jpg");  //不设置这个 使用地址会变成下载,这个格式是用来浏览的
                putObjectRequest.setMetadata(metadata);

                //上传
                ossClient.putObject(putObjectRequest);
                strings.add("http://maixuan88.oss-cn-hangzhou.aliyuncs.com/"+fileName);
            }

        } catch (OSSException oe) {
            System.out.println("捕获了一个OSSException,这意味着你的请求使它到OSS,但由于某种原因错误响应被拒绝");
            System.out.println("报错类型:" + oe.getErrorMessage());
            System.out.println("报错原因:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("捕获了一个ClientException,这意味着客户端遇到了试图与OSS沟通时出现了严重的内部问题,例如不能访问网络。");
            System.out.println("Error Message:" + ce.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return strings;
    }

    /**
     * 下载OSS服务器的文件
     *
     * @param fileName
     * @param response
     */
    @RequestMapping(value = "/downOSSFile",method = RequestMethod.GET)
    @ResponseBody
    public void downOSSFile(String fileName, HttpServletResponse response) {

        // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // 阿里云账号access Key和access Id
        String accessKeyId = "LTAxxxxxxxx";
        String accessKeySecret = "Gc4xxxxxx";
        // 填写Bucket名称,桶的名称
        String bucketName = "xxxx";

        BufferedInputStream input = null;
        OutputStream outputStream = null;
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        OSSObject ossObject = ossClient.getObject(bucketName, fileName);
        try {
            response.reset();
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/x-msdownload");
            response.addHeader("Content-Disposition",
                    "attachment;filename=" + new String(fileName.getBytes("gb2312"), "ISO8859-1"));

            input = new BufferedInputStream(ossObject.getObjectContent());
            byte[] buffBytes = new byte[1024];
            outputStream = response.getOutputStream();
            int read = 0;
            while ((read = input.read(buffBytes)) != -1) {
                outputStream.write(buffBytes, 0, read);
            }
            outputStream.flush();
            // 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
            ossObject.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
                if (input != null) {
                    input.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        ossClient.shutdown();
    }

}

浏览:

视频的压缩

必要的依赖

   <!-- 视频压缩 -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-core</artifactId>
            <version>3.0.0</version>
        </dependency>
        <!-- 在windows上开发 开发机可实现压缩效果 window64位 -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-win64</artifactId>
            <version>3.0.0</version>
        </dependency>
        <!-- 在linux上部署 linux服务器需要这个才能生效 linux64位 -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-linux64</artifactId>
            <version>3.0.0</version>
        </dependency>

代码


    //判断是否为视频文件
    private static boolean IsVideo(String fileName) {

        Boolean isVideo = false;
        String[] formateList = {"avi","flv","mpg","mpeg","mpe","m1v","m2v","mpv2","mp2v","dat","ts","tp","tpr","pva","pss","mp4","m4v",
                "m4p","m4b","3gp","3gpp","3g2","3gp2","ogg","mov","qt","amr","rm","ram","rmvb","rpm"};

        for (String f : formateList){
            if (fileName.split("\\.")[1].equals(f)) {
                isVideo = true;
                break;
            }
        }

        return isVideo;
    }

    //判断是否为图片
    public static Boolean isPicTure(String fileName){
        boolean flag = false;
        if (StringUtils.hasText(fileName)){
            flag = false;
        }
        String[] imgArr = {".bmp",".jpg",".png",".tif",".gif",".pcx",".tga",".exif",".fpx",".svg",".psd",".cdr"};
        for (String s : imgArr){
            if (fileName.split("\\.")[1].equals(s)) {
                flag = true;
                break;
            }
        }
        return flag;
    }

压缩


    /**
     * 传视频File对象(这是一个具体的文件),返回压缩后File对象信息
     * @param source
     */
    public static File compressionVideo(File source,String picName) {
        if(source == null){
            return null;
        }
        String newPath = source.getAbsolutePath().substring(0, source.getAbsolutePath().lastIndexOf(File.separator)).concat(File.separator).concat(picName);
        File target = new File(newPath);
        try {
            MultimediaObject object = new MultimediaObject(source);
            AudioInfo audioInfo = object.getInfo().getAudio();
            // 根据视频大小来判断是否需要进行压缩,
            int maxSize = 5;
            double mb = Math.ceil(source.length()/ 1048576);
            int second = (int)object.getInfo().getDuration()/1000;
            BigDecimal bd = new BigDecimal(String.format("%.4f", mb/second));
            System.out.println("开始压缩视频了--> 视频每秒平均 "+ bd +" MB ");
            // 视频 > 5MB, 或者每秒 > 0.5 MB 才做压缩, 不需要的话可以把判断去掉
            boolean temp = mb > maxSize || bd.compareTo(new BigDecimal(0.5)) > 0;
//            if(temp){
            long time = System.currentTimeMillis();
            //TODO 视频属性设置
            int maxBitRate = 128000;
            int maxSamplingRate = 44100;
            int bitRate = 800000;
            int maxFrameRate = 20;
            int maxWidth = 1280;

            AudioAttributes audio = new AudioAttributes();
            // 设置通用编码格式10                   audio.setCodec("aac");
            // 设置最大值:比特率越高,清晰度/音质越好
            // 设置音频比特率,单位:b (比特率越高,清晰度/音质越好,当然文件也就越大 128000 = 182kb)
            if(audioInfo.getBitRate() > maxBitRate){
                audio.setBitRate(new Integer(maxBitRate));
            }

            // 设置重新编码的音频流中使用的声道数(1 =单声道,2 = 双声道(立体声))。如果未设置任何声道值,则编码器将选择默认值 0。
            audio.setChannels(audioInfo.getChannels());
            // 采样率越高声音的还原度越好,文件越大
            // 设置音频采样率,单位:赫兹 hz
            // 设置编码时候的音量值,未设置为0,如果256,则音量值不会改变
            // audio.setVolume(256);
            if(audioInfo.getSamplingRate() > maxSamplingRate){
                audio.setSamplingRate(maxSamplingRate);
            }

            //TODO 视频编码属性配置
            ws.schild.jave.info.VideoInfo videoInfo = object.getInfo().getVideo();
            VideoAttributes video = new VideoAttributes();
            video.setCodec("h264");
            //设置音频比特率,单位:b (比特率越高,清晰度/音质越好,当然文件也就越大 800000 = 800kb)
            if(videoInfo.getBitRate() > bitRate){
                video.setBitRate(bitRate);
            }

            // 视频帧率:15 f / s  帧率越低,效果越差
            // 设置视频帧率(帧率越低,视频会出现断层,越高让人感觉越连续),视频帧率(Frame rate)是用于测量显示帧数的量度。所谓的测量单位为每秒显示帧数(Frames per Second,简:FPS)或“赫兹”(Hz)。
            if(videoInfo.getFrameRate() > maxFrameRate){
                video.setFrameRate(maxFrameRate);
            }

            // 限制视频宽高
            int width = videoInfo.getSize().getWidth();
            int height = videoInfo.getSize().getHeight();
            if(width > maxWidth){
                float rat = (float) width / maxWidth;
                video.setSize(new VideoSize(maxWidth,(int)(height/rat)));
            }

            EncodingAttributes attr = new EncodingAttributes();
//                attr.setFormat("mp4");
            attr.setAudioAttributes(audio);
            attr.setVideoAttributes(video);

            // 速度最快的压缩方式, 压缩速度 从快到慢: ultrafast, superfast, veryfast, faster, fast, medium,  slow, slower, veryslow and placebo.
//                attr.setPreset(PresetUtil.VERYFAST);
//                attr.setCrf(27);
//                // 设置线程数
//                attr.setEncodingThreads(Runtime.getRuntime().availableProcessors()/2);

            Encoder encoder = new Encoder();
            encoder.encode(new MultimediaObject(source), target, attr);
            System.out.println("压缩总耗时:" + (System.currentTimeMillis() - time)/1000);
            return target;
//            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(target.length() > 0){
                source.delete();
            }
        }
        return source;
    }

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值