JAVA将上传的PPT/PPTX转为图片

前台略,从后台开始写

public Map<String, String> saveDocument(HttpServletRequest request, CommonsMultipartFile myfiles) throws Exception {
		Map<String, String> map = new HashMap<String, String>();
		String existId = request.getParameter("id");//获取前台传的id,更新时使用
		if(!StringUtils.isNotBlank(existId)){
			//是更新,删除原文件
			//首先将该ID的文件全部删除
			FileUtil.delete(new File(PropsUtil.getProperty("document.actualPath") + existId +".zip"));
			FileUtil.deltree(new File(PropsUtil.getProperty("document.actualIconPath") + existId ));
		}
		/*// 文件保存在tomcat
		String realPaths = request.getServletContext().getRealPath("");
		String contentPath = realPaths.replaceAll("\\\\", "/");
		String finalFilePath = contentPath.substring(0, contentPath.lastIndexOf("/"));*/

		if (myfiles != null && !myfiles.isEmpty()) {
			String originalFileName = myfiles.getOriginalFilename();
			if (originalFileName != null && !originalFileName.equals("")) {

				String[] fileNameArr = originalFileName.split("\\.");
				String suffix = fileNameArr[fileNameArr.length - 1];
				if (null != suffix && !"".equals(suffix) && ("zip".equals(suffix) || "ppt".equals(suffix)
						|| "pptx".equals(suffix) || "pdf".equals(suffix) || "mp4".equals(suffix))) {

					String fileNames = fileNameArr[fileNameArr.length - 2];
					//如果existId存在说明是更新,不需要检查标题重复
					if(StringUtils.isNotBlank(existId)){
						Boolean falg = findRepeatTitleWithUpdate(existId,fileNames);
						if(falg){
							//此时说明数据库中已经存在相同的标题,本项目限制文件标题名不能重复,更新时,同文件名可以重复
							map.put("code", "failed");
							map.put("msg", "上传文件标题已存在,请修改压缩文件里config.json文件的title值");
							return map;
						}
					}else{
						//验证文件主题是否重复,若重复则返回错误
						Boolean findRepeatTitle = findRepeatTitle(fileNames);
						if(findRepeatTitle){
							//此时说明数据库中已经存在相同的标题,
							map.put("code", "failed");
							map.put("msg", "上传文件标题已存在,请修改压缩文件里config.json文件的title值");
							return map;
						}
					}
					String ftpPath = PropsUtil.getProperty("document.FirstPath");
					//先保存文件名,获取该文件保存到数据库中的id
					Document dDto = null;
					if(!StringUtils.isNotBlank(existId)){
						dDto = saveDoc(fileNames);//当是添加时,重新生成ID,留后使用
					}else{
						dDto = new Document();//当是更新时,ID已存在,直接赋值
						dDto.setId(existId);
					}
					Long DocSize = myfiles.getSize();
					if ("ppt".equals(suffix) || "pptx".equals(suffix)) {
						String zipFilePath = PropsUtil.getProperty("document.FirstPath");
						DocumentConfigVo dcDto = new DocumentConfigVo();//项目要求的保存文件格式
						dcDto.setType("SEDA");//文件类型
						dDto.setDocument_type("SEDA");
						dDto.setDocument_title(fileNames);
						dcDto.setTitle(fileNames);
						String faceImgName = "icon";
						String imgSuffix = "png";
						//文件最终存放位置
						File serverRoot = new File(PropsUtil.getProperty("document.actualPath"));
						@SuppressWarnings("unused")
						String destZipFile = ftpPath;
						String tmpDirName = ftpPath + File.separator + dDto.getId();
						String tmpAppDirName = tmpDirName + File.separator + "app";
						String tmpPicsDirName = tmpAppDirName + File.separator + "pics";
						String tmpThumsDirName = tmpAppDirName + File.separator + "thums";
						File tmpDir = new File(tmpDirName);
						File tmpPicsDir = new File(tmpPicsDirName);
						File tmpTumbsDir = new File(tmpThumsDirName);
						//文件夹不存在则自动创建
						createTempDirector(tmpDir, tmpPicsDir, tmpTumbsDir, serverRoot);//自动创建文件的
						/*InputStream slidesInputStream = myfiles.getInputStream();
						// ppt临时文件
						File tempSlides = new File(tmpPicsDir + File.separator, originalFileName);
						FileUtils.copyInputStreamToFile(slidesInputStream, tempSlides);*/
						myfiles.transferTo(new File(tmpPicsDir + File.separator+originalFileName));
						// 根据ppt临时文件生成图片
						if("pptx".equals(suffix)){
							PPTToImageUtil.ppt2007Img(tmpPicsDir + File.separator + originalFileName, tmpPicsDirName);
						}
						if("ppt".equals(suffix)){
							PPTToImageUtil.ppt2003Img(tmpPicsDir + File.separator + originalFileName, tmpPicsDirName);
						}
						//删除原文件
						FileUtil.delete(tmpPicsDirName + File.separator + originalFileName);
						File[] files = tmpPicsDir.listFiles();
						int i = 0;
						List<String> thumsPaths = new ArrayList<String>();
						List<String> picsPaths = new ArrayList<String>();
						File firstFile = null;
						for (File f : files) {
							System.out.println(f.getName());
							String name = PPTToImageUtil.formatNum(f.getName());
							PPTToImageUtil.renameFile(f.getParent(), name + ".png", f.getName());
							f = new File(f.getParent() + File.separator + name + ".png");
							BufferedImage bi = ImageIO.read(f);
							if ("1.png".equals(f.getName())) {
								Thumbnails.of(bi).size(128, 128).keepAspectRatio(false).toFile(tmpAppDirName + File.separator + faceImgName + "." + imgSuffix);
								dcDto.setIcon("app" + "/" + faceImgName + "." + imgSuffix);
								dDto.setDocument_icon(dDto.getId() + "/" + faceImgName + "." + imgSuffix);
							}
							// 生成缩略图Thumbnails要导入net.coobird.thumbnailator.Thumbnails包

							Thumbnails.of(bi).size(128, 128).keepAspectRatio(false).toFile(tmpThumsDirName + File.separator + f.getName());
							thumsPaths.add("app" + "/" + "thums" + "/" + (i + 1) + ".png");
							picsPaths.add("app" + "/" + "pics" + "/" + (i + 1) + ".png");
							if (i == 0) {
								firstFile = f;
							}
							i++;
						}
						dDto.setDocument_version(1);
						dDto.setDocument_size(DocSize.intValue());
						dDto.setDocument_download_count(0);
						dcDto.setPages(Integer.toString(i));
						dDto.setDocument_page_count(i);
						dcDto.setThums(thumsPaths);
						dcDto.setPics(picsPaths);
						List<DocumentConfigVo> dcDtos = new ArrayList<DocumentConfigVo>();
						dcDtos.add(dcDto);
						//String JsonStr = JsonHandler.objectToJson(dcDtos);
						String JsonStr = JSONArray.fromObject(dcDtos).toString();
						writeJsonToFile(JsonStr, tmpDirName);//将配置文件写入到磁盘
						FileUtils.deleteQuietly(new File(tmpPicsDirName + File.separator + originalFileName));
						//将icon.png移到最终目标位置
						File ff = new File(PropsUtil.getProperty("document.actualIconPath"));
						if(!ff.exists()){
							ff.mkdirs();
						}
						FileUtil.copyFile(new File(tmpAppDirName + File.separator + faceImgName + "." + imgSuffix), new File(PropsUtil.getProperty("document.actualIconPath")+ dDto.getId() +File.separator + faceImgName + "." + imgSuffix));
						try {
							Zip4jUtils.zip(tmpDirName, serverRoot + File.separator + dDto.getId() + ".zip", null);//导入net.lingala.zip4j...包
							/*InputStream isFirst = new FileInputStream(firstFile);
							File file_first = new File(finalFilePath + File.separator + "download" + File.separator
									+ dDto.getId() + "/icon" + "." + imgSuffix);
							if (!file_first.exists()) {
								file_first.createNewFile();
							}
							OutputStream osFirst = new FileOutputStream(file_first);
							byte[] b = new byte[1024];// 新建一个字节数组
							int mn = 0;
							while ((mn = isFirst.read(b)) != -1) {
								osFirst.write(mn);
							}
							isFirst.close();
							osFirst.close();*/
							FileUtils.deleteDirectory(new File(tmpDirName));
							dDto.setDocument_download_path(dDto.getId() + "/" + dDto.getId() + ".zip");
							// MD5加密
							File file = new File(serverRoot + File.separator + dDto.getId() + ".zip");
							String Md5File = getMD5(file);
							dDto.setDocument_md5_sum(Md5File);
							dDto.setDocument_is_new(0);
							dDto.setDocument_upload_cdn(0);
							dDto.setDocument_local_path(PropsUtil.getProperty("document.actualPath")+dDto.getId()+".zip");
							saveEntity(dDto);
							map.put("code", "SUCCESS");
							map.put("msg","文件上传成功");
						} catch (ZipException e) {
							map.put("code", "failed");
							map.put("msg","压缩文件失败");
							throw new RestException("压缩文件失败");
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} finally {
							FileUtil.deltree(zipFilePath + File.separator + dDto.getId());
						}
					}
如果是更新文件,会用到方法findRepeatTitleWithUpdate

public Boolean findRepeatTitleWithUpdate(String existId, String fileNames) {
		List<Document> docs = documentDao.findRepeatTitle(fileNames);
		if(docs.size() == 0){
			//无重复
			return false;
		}else{
			//有重复
			//和更新的文件标题一样,不报错
			if(docs.size() == 1){
				for(Document ee: docs){
					if(StringUtils.equals(ee.getId(), existId)){
						return false;
					}
					return true;
				}
			}else{
				return true;
			}
		}
		return null;
		
	}
PPTToImageUtil工具类

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.imageio.ImageIO;

import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.model.Slide;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFShape;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.poi.xslf.usermodel.XSLFTextParagraph;
import org.apache.poi.xslf.usermodel.XSLFTextRun;
import org.apache.poi.xslf.usermodel.XSLFTextShape;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide;

public class PPTToImageUtil {
public static final int ppSaveAsPNG = 17;
	
	public static void main(String[] args) {
		doPPTtoImage2007(new File("D:\\upload_1\\Java_Concurrent_Example.pptx"), "D:\\ppt", "a", "png");
	}
	/**
	 * 设置PPTX字体 
	 * @param slide
	 */
    private static void setFont(XSLFSlide slide) {
        for (XSLFShape shape : slide.getShapes()) {
            if (shape instanceof XSLFTextShape) {
            	XSLFTextShape txtshape = (XSLFTextShape)shape ;
                for (XSLFTextParagraph paragraph : txtshape.getTextParagraphs()) {
                    List<XSLFTextRun> truns = paragraph.getTextRuns();
                    for (XSLFTextRun trun : truns) {
                            trun.setFontFamily("宋体");
                    }
                }
            }

        }
    }
	/**
	* PPT转图片 (jpeg)(2007)
	* @param file
	* @param path 存放路径
	* @param picName 图片前缀名称 如 a 生成后为a_1,a_2 ...
	* @param picType 转成图片的类型,无点 如 jpg bmp png ...
	* @return true/false
	*/
	public static boolean doPPTtoImage2007(File file,String path,String picName,String picType) {
		try {
			boolean isppt = checkFile(file);
			if (!isppt) {
			return false;
			}
			FileInputStream is = new FileInputStream(file);
			XMLSlideShow xmlSlideShow = new XMLSlideShow(is);
			XSLFSlide[] xslfSlides = xmlSlideShow.getSlides();
			Dimension pageSize = xmlSlideShow.getPageSize();
		
			is.close();
			for (int i = 0; i < xslfSlides.length; i++) {
				System.out.print("第" + i + "页。");
				setFont(xslfSlides[i]);
				BufferedImage img = new BufferedImage(pageSize.width,
				pageSize.height, BufferedImage.TYPE_INT_RGB);
			
				Graphics2D graphics = img.createGraphics();
				graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
						RenderingHints.VALUE_ANTIALIAS_ON);
				graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
						RenderingHints.VALUE_RENDER_QUALITY);
				graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
						RenderingHints.VALUE_INTERPOLATION_BICUBIC);
				graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
						RenderingHints.VALUE_FRACTIONALMETRICS_ON);
				
				graphics.setPaint(Color.white);
				graphics.fill(new Rectangle2D.Float(0, 0, pageSize.width,
				pageSize.height));
				xslfSlides[i].draw(graphics);
				FileOutputStream out = new FileOutputStream(path+"/"+
				+ (i + 1) + "."+picType);
				javax.imageio.ImageIO.write(img, "jpeg", out);
				out.close();
			}
			return true;
		} catch (Exception e) {
		   e.printStackTrace();
		}
		return false;
	}
	/**
	* PPT转图片 (jpeg)(2003)
	* @param file
	* @param path 存放路径
	* @param picName 图片前缀名称 如 a 生成后为a_1,a_2 ...
	* @param picType 转成图片的类型,无点 如 jpg bmp png ...
	* @return true/false
	*/
	public static boolean doPPTtoImage(File file,String path,String picName,String picType) {
		boolean isppt = checkFile(file);
		if (!isppt) {
			return false;
		}
		try {
			FileInputStream is = new FileInputStream(file);
			SlideShow ppt = new SlideShow(is);
			is.close();
			// Dimension pgsize = ppt.getPageSize();
			Slide[] slide = ppt.getSlides();
			Dimension pgSize = ppt.getPageSize();
			for (int i = 0; i < slide.length; i++) {
				System.out.print("第" + i + "页。");
				BufferedImage img = new BufferedImage(pgSize.width,
						pgSize.height, BufferedImage.TYPE_INT_RGB);
			
				Graphics2D graphics = img.createGraphics();
				graphics.setPaint(Color.white);
				graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
						RenderingHints.VALUE_ANTIALIAS_ON);
				graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
						RenderingHints.VALUE_RENDER_QUALITY);
				graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
						RenderingHints.VALUE_INTERPOLATION_BICUBIC);
				graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
						RenderingHints.VALUE_FRACTIONALMETRICS_ON);
				graphics.fill(new Rectangle2D.Float(0, 0, pgSize.width,
						pgSize.height));
				slide[i].draw(graphics);
			
				FileOutputStream out = new FileOutputStream(path+"/"
				+ (i + 1) + "."+picType);
				javax.imageio.ImageIO.write(img, "jpeg", out);
				out.close();
			}
			return true;
		} catch (FileNotFoundException e) {
		    System.out.println(e);
		} catch (IOException e) {
		}
		return false;
	}
	
	/**
	* PPT转图片 (jpeg)(2003)
	* @param file
	* @param path 存放路径
	* @param picName 图片前缀名称 如 a 生成后为a_1,a_2 ...
	* @param picType 转成图片的类型,无点 如 jpg bmp png ...
	* @return true/false
	*/
	public static boolean doPPTtoImage(File file,String path,String picName,String picType, int w, int h) {
		boolean isppt = checkFile(file);
		if (!isppt) {
			return false;
		}
		try {
			FileInputStream is = new FileInputStream(file);
			SlideShow ppt = new SlideShow(is);
			is.close();
			// Dimension pgsize = ppt.getPageSize();
			Slide[] slide = ppt.getSlides();
			for (int i = 0; i < slide.length; i++) {
				System.out.print("第" + i + "页。");
				BufferedImage img = new BufferedImage(w,
						h, BufferedImage.TYPE_INT_RGB);
			
				Graphics2D graphics = img.createGraphics();
				graphics.setPaint(Color.white);
				graphics.fill(new Rectangle2D.Float(0, 0, w,
						h));
				slide[i].draw(graphics);
			
				FileOutputStream out = new FileOutputStream(path+"/"+picName+"_"
				+ (i + 1) + "."+picType);
				javax.imageio.ImageIO.write(img, "jpeg", out);
				out.close();
			}
			return true;
		} catch (FileNotFoundException e) {
		    System.out.println(e);
		} catch (IOException e) {
		}
		return false;
	}
	
	private static boolean checkFile(File file) {
		int pos = file.getName().lastIndexOf(".");
		String extName = "";
		if(pos >= 0) {
			 extName = file.getName().substring(pos);
		}
		if(".ppt".equalsIgnoreCase(extName) || ".pptx".equalsIgnoreCase(extName)){
			return true;
		}
		return false;
	} 
	/**
	 * 根据ppt文件生成图片
	 * @param string
	 * @param pdfFile
	 */
	@SuppressWarnings("deprecation")
	public static synchronized void ppt2007Img(String sourceFile,String pdfFile){
		try {
			FileInputStream is = new FileInputStream(sourceFile);
		    XMLSlideShow ppt = new XMLSlideShow(is);
		    is.close();
		    Dimension pgsize = ppt.getPageSize();
		    XSLFSlide[] slides = ppt.getSlides();
		    for(int i=0;i<slides.length;i++){
		    	 System.out.print("第" + i + "页。");
		    	 //设置字体为宋体,解决中文乱码问题
		    	CTSlide xmlObject = slides[i].getXmlObject();
		    	CTGroupShape spTree = xmlObject.getCSld().getSpTree();
		    	CTShape[] spArray = spTree.getSpArray();
		    	 for (CTShape shape : spArray) {
		    		 CTTextBody txBody = shape.getTxBody();
		    		 if(txBody == null){
		    			 continue;
		    		 }
		    		 CTTextParagraph[] pArray = txBody.getPArray();
		    		 CTTextFont font=CTTextFont.Factory.parse(
		                        "<xml-fragment xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\">"+
		                        "<a:rPr lang=\"zh-CN\" altLang=\"en-US\" dirty=\"0\" smtClean=\"0\"> "+
		                            "<a:latin typeface=\"+mj-ea\"/> "+
		                          "</a:rPr>"+
		                        "</xml-fragment>");
		    		 for (CTTextParagraph textParagraph : pArray) {
		                    CTRegularTextRun[] textRuns = textParagraph.getRArray();
		                    for (CTRegularTextRun textRun : textRuns) {
		                        CTTextCharacterProperties properties=textRun.getRPr();
		                        properties.setLatin(font);
		                    }
		                }
		    	 }
		    	BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB);
		    	Graphics2D graphics = img.createGraphics();
		    	graphics.setPaint(Color.white);
		        graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
		        slides[i].draw(graphics);
		        FileOutputStream out = new FileOutputStream(new File(pdfFile+File.separator+i +".png"));
		        ImageIO.write(img, "png", out);
		        out.close();
		    }
		    
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (XmlException e) {
			e.printStackTrace();
		}
    }
	public static void renameFile(String path,String newName,String olName){
		if(!olName.equals(newName)){//新的文件名和以前文件名不同时,才有必要进行重命名 
            File oldfile=new File(path+"/"+olName); 
            File newfile=new File(path+"/"+newName); 
            if(!oldfile.exists()){
                return;//重命名文件不存在
            }
            if(newfile.exists())//若在该目录下已经有一个文件和新文件名相同,则不允许重命名 
                System.out.println(newName+"已经存在!");
            else{ 
                oldfile.renameTo(newfile);
            } 
        }else{
            System.out.println("新文件名和旧文件名相同...");
        }
	}
	public static String formatNum(String name){
		int index =0;
		Pattern pattern = Pattern.compile("[0-9]");
		Matcher matcher = pattern.matcher(name);
		if (matcher.find()) {
			index = name.indexOf(matcher.group());
	     }
		name = name.substring(index);
		name = name.substring(0,name.lastIndexOf("."));
		System.out.println(name);
		return name;
	}
	public static void ppt2003Img(String sourceFile, String tmpPicsDirName) {
		try {
			SlideShow ppt = new SlideShow(new HSLFSlideShow(sourceFile));
		    Dimension pgsize = ppt.getPageSize();
		    Slide[] slides = ppt.getSlides();
		    for(int i=0;i<slides.length;i++){
		    	 System.out.print("第" + i + "页。");
		    	BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB);
		    	Graphics2D graphics = img.createGraphics();
		    	graphics.setPaint(Color.white);
		        graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
		        slides[i].draw(graphics);
		        FileOutputStream out = new FileOutputStream(new File(tmpPicsDirName+File.separator+i +".png"));
		        ImageIO.write(img, "png", out);
		        out.close();
		    }
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} 
		
	}
	
}
将生成的数据写到磁盘文件中writeJsonToFile方法

private void writeJsonToFile(String jsonStr, String tmpDirName) throws IOException {
		FileOutputStream writerStream = new FileOutputStream(tmpDirName + File.separator + "config.json");
		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(writerStream, "UTF-8"));
		writer.write(jsonStr);
		writer.close();
		writerStream.close();
	}
对文件进行MD5加密,getMD5方法(文件的MD5码)

private static String getMD5(File file) {
		int len = 0;
		try {
			MessageDigest messageDigest = MessageDigest.getInstance("MD5");
			InputStream inputStream = new FileInputStream(file);
			// 获取文件md5值
			byte[] buffer = new byte[1024];
			while ((len = inputStream.read(buffer)) != -1) {
				messageDigest.update(buffer, 0, len);
			}
			BigInteger bigInteger = new BigInteger(1, messageDigest.digest());
			String b = bigInteger.toString(16);
			for (int i = b.length(); i < 32; i++) {
				b = "0" + b;
			}
			inputStream.close();
			return b;
		} catch (Exception e) {
			return null;
		}
	}














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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值