IO 操作 (二进制流文件上传下载)

后台 二进制流 -> 前台blob对象 ->生成 dateUrl->前台 file->dataUrl -blob ->FormData ->后台

java 后台返回pdf的二进制流

    @RequestMapping("/showPdf")
    public void showPdf(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        response.setContentType("application/pdf");
        FileInputStream in;
        OutputStream out;
        try {
            //in = new FileInputStream(new File(request.getParameter("path")));
            in = new FileInputStream("D:\\VW481_ZSB_数据三方列表_FK.pdf");
            out = response.getOutputStream();
            byte[] b = new byte[512];
            while ((in.read(b)) != -1) {
                out.write(b);
            }
            out.flush();
            in.close();
            out.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

vue 前台根据blob对象解析后台返回的二进制流

    loadPdfHandler (row) {
        this.$http({
          url: `cockpit/piwebPartnfo/showPdf`,
          method: 'get',
          //responseType: 'arraybuffer',   //一定要设置响应类型,否则页面会是空白pdf
          //responseType:"multipart/form-data",
          responseType: 'blob',
          params: { url: '1111111', lang: 'en_US' },

          headers: {
              'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
          }

        }).then(res => {
          const binaryData = [];
          binaryData.push(res.data);
          //获取blob链接
          this.pdfObj.pdfSrc = this.getObjectURL(res.data);
          this.showUnitPdf = true;
          this.pdfObj.currentPage = 1 // 加载的时候先加载第一页
          console.log()
        });
          //this.pdfObj.pdfSrc = "../../VW481_ZSB_数据三方列表_FK.pdf";

      },
      getObjectURL(file) {
        let url = null
        if (window.createObjectURL !== undefined) { // basic
          url = window.createObjectURL(file)
        } else if (window.webkitURL !== undefined) { // webkit or chrome
          try {
            url = window.webkitURL.createObjectURL(file)
          } catch (error) {
            console.log(error)
          }
        } else if (window.URL !== undefined) { // mozilla(firefox)
          try {
            url = window.URL.createObjectURL(file)
          } catch (error) {
            console.log(error)
          }
        }
        return url
      },

java 后台返回 base64加密的图片流

    static BASE64Encoder encoder = new sun.misc.BASE64Encoder();
    static BASE64Decoder decoder = new sun.misc.BASE64Decoder();

  private String getImageBinary(InputStream  inputStream){
        //File f = new File("f://123456.jpg");	//这里gif动态图不可以,虽然在后面也能输出gif格式,但是却不是动图
        BufferedImage bi;
        try {
            bi = ImageIO.read(inputStream);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(bi, "jpg", baos);
            byte[] bytes = baos.toByteArray();

            return encoder.encodeBuffer(bytes).trim();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

处理base64 加密的 二进制流生成blob对象

图片直接展示 base64加密的 字节流

img.src = “data:image/gif;base64,”+base64Str;

      getBase64toDataUrl(base64Str) {
      
        var bstr = this.decode(base64Str),
          n = bstr.length,
          u8arr = new Uint8Array(n),
          mime = "data:image/jpeg;base64";  //文件类型 blob 使用
        while (n--) {
          u8arr[n] = bstr.charCodeAt(n);
        }
        var blod = new Blob([u8arr], {
          type: mime
        });

        let URL = window.URL || window.webkitURL
        let objectUrl = URL.createObjectURL(blod)

        return objectUrl;
      },
      //解密方法
       decode (input) {
        var _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
        var output = ''
        var chr1, chr2, chr3
        var enc1, enc2, enc3, enc4
        var i = 0
        var re = /[^A-Za-z0-9\+\/\=]/g;
        input = input.replace(re, '')
        while (i < input.length) {
          enc1 = _keyStr.indexOf(input.charAt(i++))
          enc2 = _keyStr.indexOf(input.charAt(i++))
          enc3 = _keyStr.indexOf(input.charAt(i++))
          enc4 = _keyStr.indexOf(input.charAt(i++))
          chr1 = (enc1 << 2) | (enc2 >> 4)
          chr2 = ((enc2 & 15) << 4) | (enc3 >> 2)
          chr3 = ((enc3 & 3) << 6) | enc4
          output = output + String.fromCharCode(chr1)
          if (enc3 !== 64) {
            output = output + String.fromCharCode(chr2)
          }
          if (enc4 !== 64) {
            output = output + String.fromCharCode(chr3)
          }
        }
        output = this._utf8_decode(output)
        return output
      },
      _utf8_decode(utftext) {
        let string = ''
        let i = 0
        let c = 0
        let c2 = 0
        let c3 = 0
        while (i < utftext.length) {
          c = utftext.charCodeAt(i)
          if (c < 128) {
            string += String.fromCharCode(c)
            i++
          } else if ((c > 191) && (c < 224)) {
            c2 = utftext.charCodeAt(i + 1)
            string += String.fromCharCode(((c & 31) << 6) | (c2 & 63))
            i += 2
          } else {
            c2 = utftext.charCodeAt(i + 1)
            c3 = utftext.charCodeAt(i + 2)
            string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63))
            i += 3
          }
        }
        return string
      },

流程

anvas

流程1
file  

		 
		  
fileReader     

			var reader = new FileReader();
          // 传入一个参数对象即可得到基于该参数对象的文本内容
          reader.onload = function(e){
            // target.result 该属性表示目标对象的DataURL
            that.imageDataUrl = e.target.result
          }
          reader.readAsDataURL(file);      

DataURL  
		//dataUrl -> blob 对象
		var  data = dataUrl.split(',')[1];
        data = window.atob(data);
        var ia = new Uint8Array(data.length);
        for (var i = 0; i < data.length; i++) {
          ia[i] = data.charCodeAt(i);
        }
        return new Blob([ia], {
          type: "image/jpeg"
        });
			
	// 2 不固定
		let arr = this.cutAvater.split(',')
	    let data = window.atob(arr[1])
	    let mime = arr[0].match(/:(.*?);/)[1]
	    let ia = new Uint8Array(data.length)
	    for (var i = 0; i < data.length; i++) {
	      ia[i] = data.charCodeAt(i)
	    }
	    this.blob = new Blob([ia], {type: mime})
	

 blob
		 var oMyForm = new FormData();
			 oMyForm.append(imgeInfoList[x].key, imgeInfoList[x].blob, fileName);
			 oMyForm.append("fileType", 'image');
FormData 
		
				this.$http.post('onlineDataReport/upload',oMyform,{
						'Content-Type':'multipart/form-data'
				}).then(res=>{

				})
request  java
		通过Request 获取文件信息和参数
		Gson gson= new Gson();
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        String baseInfo = multipartRequest.getParameter("baseInfo");

        ReportEntity reportEntity = gson.fromJson(baseInfo, ReportEntity.class);
        
        reportEntity.setDataMap(gson.fromJson(reportEntity.getDataMapStr(),Map.class));
        
        MultiValueMap<String, MultipartFile> multiValueMap = multipartRequest.getMultiFileMap();
        List<MultipartFile> srcfiles = multiValueMap.get("srcImageFile");//原图片
        List<MultipartFile> reportfiles = multiValueMap.get("reportImageFile"); //报表文件
流程2
base64字节流 ->  blob  ->  dataUrl

请求类型

‘Content-Type’: ‘multipart/form-data’ // 处理文件

响应类型

responseType: ‘blob’,

后台响应头设置
response.setContentType(“application/pdf”); 输出二进制流

java 后端restTemp 进行文件上传下载

调用第三方接口上传文件

ByteArrayResource   HttpEntity    HttpHeaders    multipart/form-data   Content-Length
         MultipartFile upfile 

        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "multipart/form-data");
        headers.add("Content-Length",String.valueOf(upfile.getSize()));

        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        try {
            byte[] bytes = upfile.getBytes();
            ByteArrayResource resource = new ByteArrayResource(bytes) {
                @Override
                public String getFilename() {
                    return upfile.getOriginalFilename();
                }
            };
            form.add("upfile",resource);
        } catch (IOException e) {
            e.printStackTrace();
        }

		HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);
		UpLoadResult upLoadResult = restTemplate.postForObject("http://audi-ep-bi-file-server/excelTemp/up-file", files, UpLoadResult.class);

@Data
public class UpLoadResult  implements Serializable {
    private static final long serialVersionUID = -8227277408478690545L;
    private String code;
    private String msg;
    private String path;
    private String id;
    private String fileName;
}

调用第三方接口下载文件

  String userAgent = request.getHeader("User-Agent");
                // 针对IE或者以IE为内核的浏览器:
                if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
                    fileName = URLEncoder.encode(fileName, "UTF-8");
                } else {
                    // 非IE浏览器的处理:
                    fileName = new String(fileName.getBytes(), "ISO8859-1");
                }
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/octet-stream");
                response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
                //response.setContentType("application/vnd.ms-excel;charset=utf-8");
                //response.setHeader("Content-Disposition","attachment;filename=" + fileName);
                response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
                ServletOutputStream out = response.getOutputStream();

 ResponseEntity<String> exchange = restTemplate.exchange("http://audi-ep-bi-file-server/excelTempFs/exist/" +     tableStructure.getTempFileId(), HttpMethod.GET, null, String.class);
                    if(exchange.getStatusCode().value()==200){
                        //定义请求头的接收类型  调用文件下载接口下载文件
                        RequestCallback requestCallback = req -> req.getHeaders()
                                .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
                        //对响应进行流式处理而不是将其全部加载到内存中
                        String execute = restTemplate.execute("http://audi-ep-bi-file-server/excelTempFs/" + tableStructure.getTempFileId(), HttpMethod.GET, requestCallback, clientHttpResponse -> {
                            InputStream inputStream = clientHttpResponse.getBody();
                            writeFile(response, inputStream);
                            return "success";
                        });
                        return;
                    }


 public  void writeFile(HttpServletResponse resp, InputStream inputStream) {
        OutputStream out = null;
        try {
            out = resp.getOutputStream();
            int len = 0;
            byte[] b = new byte[1024];
            while ((len = inputStream.read(b)) != -1) {
                out.write(b, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

Base64加密字符串处理上传

public class IoTest {

    public static void main(String[] args) throws Exception {
    String base64Str ="";
  
        byte[] decode =  decode(base64Str );

        ByteArrayResource resource = new ByteArrayResource(decode) {
            @Override
            public String getFilename() {
                return "aa.jpg";
            }
        };

        FileOutputStream fileOutputStream = new FileOutputStream("d://");
        writeFile(fileOutputStream,resource.getInputStream());
    }

    public static  byte[] decode(String str) {
        if (str.contains("data:")) {
            int start = str.indexOf(",");
            str = str.substring(start + 1);
        }
        final Base64.Decoder decoder = Base64.getDecoder();
        str = str.replaceAll("\r|\n", "");
        str = str.trim();
        byte[] bytes = decoder.decode(str);
        return bytes;
    }

    public static  void writeFile(OutputStream out, InputStream inputStream) {
        try {
            int len = 0;
            byte[] b = new byte[1024];
            while ((len = inputStream.read(b)) != -1) {
                out.write(b, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

泛型数据 掉通

https://blog.csdn.net/lucky_love816/article/details/121540900

  MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("page",1);
        body.add("size",5);

        HttpHeaders headers = new HttpHeaders();
        headers.add("content-type", "application/json;charset=utf-8");
        HttpEntity<?> httpEntity = new HttpEntity<>(body, headers);

        ResponseEntity<RpcResponse<PageData<UserDTO>>> responseEntity = null;

        try  {

            responseEntity = restTemplate.exchange("http://"+serviceName+"/user/query/users", HttpMethod.GET, httpEntity, new ParameterizedTypeReference<RpcResponse<PageData<UserDTO>>>() {});
            logger.debug("UserRpcService getUserByPage {}", JSONHelper.toJSON(responseEntity));
           // logger.debug("UserRpcService getUserByPage size {}",forObject.getResult().getPageData().size());
            return responseEntity.getBody();
        }
        catch (Exception e) {
            throw e;
        }

java IO 流

对象序列化
   @PostConstruct
     public void init(){
	 //读取本地的持久化的信息
	         try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"))) {
	              this.testBooks = (List<Book>) ois.readObject();
	              System.out.println(this.testBooks);
	          }catch (IOException | ClassNotFoundException e ) {
	            e.printStackTrace();
	         }
	 }


	 @PreDestroy
     public void destory(){
         try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"))) {
                oos.writeObject(this.testBooks);
         } catch (IOException e) {
                      e.printStackTrace();
         }
     }

FileReader FileWriter

		FileReader fr=new FileReader("F:\\新建文件夹\\aa.txt");//文件不存在会抛出java.io.FileNotFoundException
			FileWriter fw = new FileWriter(f);
			char chs[]=new char[1024];
			int len=0;
			while((len=fr.read(chs))!=-1) {//读数据
				fw.write(chs,0,len);//写数据
			}
			fw.flush();
			fr.close();
			fw.close()

BufferedReader BufferedWriter

			File fwrite = new File(d:\\anglize.txt);
			File f = new File(d:\\source.txt);
			if(!fwrite.exists()){
				fwrite.createNewFile();
			}
			
		BufferedReader br =null;
		BufferedWriter bw =null;
				try{
					bw = new BufferedWriter(new FileWriter(fwrite));
					br = new BufferedReader(new FileReader(f));
					String line  = null;
					while ((line = br.readLine())!=null) {
			
						 String[] strArr =  line.split("\\s");
						 Gson g = new Gson();
						 
						 value = StringUtils.trimAllWhitespace(strArr[1]);
						List<Map<String,Object>> results = g.fromJson(value,List.class);
										
						Map<String, Object> n = results.stream().filter(t -> "AS" .equals( t.get("name").toString())).findAny().orElse(null);
						String kv = n.get("currentActualA").toString();
						if(Integer.parseInt(kv) ==0){
							bw.write(strArr[0].toString());
							bw.newLine();
						}
				 }
				 
			} catch (IOException e) {
				e.printStackTrace();
			}finally {	 
				try {
					br.close();
					bw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

FileInputStream FileOutputStream

			FileInputStream inputStream = new FileInputStream("a.txt");
				FileOutputStream outputStream = new FileOutputStream("D:\\abc.txt",true);
				int len;
				// 定义字节数组,作为装字节数据的容器
				byte[] b = new byte[1024];
				// 循环读取
				while ((len = inputStream.read(b))!=-1) {
					outputStream.write(b,0,len);
				}
				inputStream.close();
				// 关闭资源
				outputStream.close();

BufferedInputStream BufferedOutputStream

		try (
				BufferedInputStream bis = new BufferedInputStream(new FileInputStream("py.exe"));
				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copyPy.exe"));
			){
				// 读写数据
				int len;
				byte[] bytes = new byte[8*1024];
				while ((len = bis.read(bytes)) != -1) {
					bos.write(bytes, 0 , len);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}

InputStreamReader InputStreamReader

		InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"),"GBK");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

		int len;
		byte[] bytes = new byte[8*1024];
		while ((len = isr2.read(bytes))!=null) {
			osw.write(bytes,0,len);
			osw.flush();
		}
		isr2.close();
		osw.close();
Nio AsynchronousFileChannel

AsynchronousFileChannel
DataBufferUtils
DataBuffer

   Path path = Paths.get("D:\\a.txt");
        try {
            if(!Files.exists(path))
                Files.createFile(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        AsynchronousFileChannel channel =
                AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        long position = 0;
        buffer.put("test data".getBytes());
        buffer.flip();
/*
        DataBufferUtils.write(dataBuffer, channel, 0)
                .doOnComplete(() -> {
                    System.out.println("finish");
                })
                .subscribe();*/

        channel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {

            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                System.out.println("bytes written: " + result);
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                System.out.println("Write failed");
                exc.printStackTrace();
            }
        });


        AsynchronousFileChannel fileChannel =
                AsynchronousFileChannel.open(path, StandardOpenOption.READ);

        fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                System.out.println("result = " + result);

                attachment.flip();
                byte[] data = new byte[attachment.limit()];
                attachment.get(data);
                System.out.println(new String(data));
                attachment.clear();
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {

            }
        });
1.7 Files Paths Path
Paths
	Path path = Paths.get("/Users/darcy/java/");
	System.out.println("完整路径:" + path.toString());
	
	Path pathParent = path.getParent();
	System.out.println("父级路径:" + pathParent.toString());
	
	Path pathRoot = path.getRoot();
	System.out.println("根目录:" + pathRoot.toString());
	
	int pathNameCount = path.getNameCount();
	System.out.println("目录深度:" + pathNameCount);
	
	Path pathIndex3 = path.getName(2);
	System.out.println("第三级目录:" + pathIndex3);
	
	Path subPath = path.subpath(1, 3);
	System.out.println("第1级目录到第三级目录(包左不包右):" + subPath.toString());
	
	// resolveSibling 从当前目录父目录开始拼接目录
	Path pathResolveSibling = path.resolveSibling("PathDemo.java");
	System.out.println("父目录开始拼接参数:" + pathResolveSibling.toString());
	
	// resolve 把当前路径当作父路径,参数作为子目录或者文件
	Path pathResolve = Paths.get("/Users/darcy/java/").resolve("PathDem.java");
	System.out.println("当前目录拼接后的目录:" + pathResolve.toString());
	
	// 参数路径相对于主体路径的相对路径
	Path path1 = Paths.get("/Users/darcy/");
	Path path2 = Paths.get("/Users/darcy/java/PathDemo.java");
	Path path3 = path1.relativize(path2);
	System.out.println("相对路径:" + path3.toString());
Files 创建 复制 读取
// 如果文件不存在,则创建一个文件
Path path = Paths.get("test.txt");
Path pathBackup = Paths.get("test_bak.txt");
Path pathLink = Paths.get("test.txt.link");
Path pathDir = Paths.get("dir");

// 已存在则删除
Files.deleteIfExists(path);
Files.deleteIfExists(pathBackup);
Files.deleteIfExists(pathLink);
Files.deleteIfExists(pathDir);

// 创建文件写入内容
Path file = Files.createFile(path);
Files.write(path, "关注公众号:程序猿阿朗".getBytes());
Files.write(path, System.lineSeparator().getBytes(), StandardOpenOption.APPEND);
Files.write(path, "欢迎加我微信:wn8398".getBytes(), StandardOpenOption.APPEND);
System.out.println("创建文件:" + file.toString());

// 创建文件链接
pathLink = Files.createLink(pathLink, path);
System.out.println("创建文件:" + pathLink.toString());

// 创建目录
Path directory = Files.createDirectory(pathDir);
System.out.println("创建目录:" + directory.toString());

// 文件复制
Files.copy(path, pathBackup);
System.out.println("复制文件: " + path + " --> " + pathBackup);

// 读取文件
List<String> lines = Files.readAllLines(pathBackup);
for (String line : lines) {
    System.out.println("文件读取:" + line);
}

文件的操作信息
Path path = Paths.get("/Users/darcy/git/jdk-feature/README.md");
BasicFileAttributeView fileAttributeView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
BasicFileAttributes basicFileAttributes = fileAttributeView.readAttributes();
FileTime creationTime = basicFileAttributes.creationTime();
FileTime lastModifiedTime = basicFileAttributes.lastModifiedTime();
FileTime lastAccessTime = basicFileAttributes.lastAccessTime();
System.out.println("创建时间:" + creationTime);
System.out.println("上次修改时间:" + lastModifiedTime);
System.out.println("上次访问时间:" + lastAccessTime);

boolean directory = basicFileAttributes.isDirectory();
boolean regularFile = basicFileAttributes.isRegularFile();
boolean symbolicLink = basicFileAttributes.isSymbolicLink();
System.out.println("是否目录:" + directory);
System.out.println("是否普通文件:" + regularFile);
System.out.println("是否符号链接:" + symbolicLink);

long size = basicFileAttributes.size();
System.out.println("文件大小:" + size);

PosixFileAttributeView linuxFileAttributeView = Files.getFileAttributeView(path, PosixFileAttributeView.class);
UserPrincipal owner = linuxFileAttributeView.getOwner();
System.out.println("文件归属用户:" + owner.getName());
文件遍历
String pathString = "/Users/darcy/project/mylab/src/main/java/com/wdbyte/java";
// Path 直接遍历方式,不会遍历子目录
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(pathString))) {
    for (Path pathTemp : directoryStream) {
        System.out.println("DirectoryStream: " + pathTemp);
    }
}

// Path 直接遍历方式 - 筛选 .class 文件
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(pathString), "*.java")) {
    for (Path pathTemp : directoryStream) {
        System.out.println("DirectoryStream file type is class : " + pathTemp);
    }
}


// 遍历所有目录和子目录
Stream<Path> pathStream = Files.walk(Paths.get("/Users/darcy/project/mylab/src/main/java/com/wdbyte"));
pathStream.forEach(pathTemp -> {
    System.out.println("Stream: " + pathTemp.toString());
});

// 遍历所有目录和子目录 - 筛选 java 文件
pathStream = Files.walk(Paths.get("/Users/darcy/project/mylab/src/main/java/com/wdbyte"));
pathStream
    .filter(pathTemp -> pathTemp.toString().endsWith(".java"))
    .forEach(pathTemp -> {
        System.out.println("Stream filter java: " + pathTemp.toString());
    });
文件监听
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值