Java 调用FFMPEG命令进行视频格式转换 (windows环境)

83 篇文章 0 订阅
7 篇文章 0 订阅

转换程序:

package com.wamei.tool;

import com.wamei.common.ResourceUtils;
import com.wamei.util.JsonResponseHelper;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 * Created by chen on 2016/8/23.
 */
public class VideoConvertUtil implements Runnable{
    private final static Logger logger = Logger.getLogger(VideoConvertUtil.class);
    public static final String FFMPEG_PATH = "D:/ffmpeg/bin/ffmpeg.exe";
    private static Process process;

    private String video_path;
    private String videoFileName;

    public VideoConvertUtil(String video_path,String videoFileName){
        this.video_path = video_path;
        this.videoFileName = videoFileName;
    }

    @Override
    public void run() {
        convertCommand(video_path,videoFileName);
    }

    /**
     * 视频转换
     * @param video_path
     * @return
     */


    public static String convertCommand(String video_path,String videoFileName) {
        //D:/ffmpeg.exe -i D:\360Downloads\7049a5246a2d44fe897c2ea1c917eeee.wmv -vcodec libx264 -preset ultrafast -profile:v baseline -acodec aac -strict experimental -s 640*480 -b 568k -ab 128k iCulture.mp4
        if(StringUtils.isEmpty(video_path)){
            return null;
        }
        File file = new File(video_path);
        if (!file.exists()) {
            System.err.println("路径[" + video_path + "]对应的视频文件不存在!");
            return null;
        }
        String videoUrl = "";
        String format = ".mp4";
        try {
//            String videoName = videoFileName.substring(0,videoFileName.lastIndexOf("."))+format;
//            String newVideoPath = video_path.substring(0,video_path.lastIndexOf("/")+1)+videoName;
            String newVideoPath = video_path.substring(0,video_path.lastIndexOf("."))+format;
            String videoName = videoFileName.substring(0,videoFileName.lastIndexOf("."))+format;
            Integer type = checkVideoType(video_path);
            logger.info("old vodeo path:"+video_path);
            logger.info("new video path"+newVideoPath);
            if(0==type){
                List<String> commands = new java.util.ArrayList<String>();
                commands.add(FFMPEG_PATH);
                commands.add("-i");
                commands.add(video_path);
                commands.add("-vcodec");
                commands.add("libx264");
                commands.add("-preset");
                commands.add("ultrafast");
                commands.add("-profile:v");
                commands.add("baseline");
                commands.add("-acodec");
                commands.add("aac");
                commands.add("-strict");
                commands.add("experimental");
//                commands.add("-s");
//                commands.add("640*480");
//                commands.add("-b");//视频品质设置(有模糊,要视频清晰使用-qscale)
//                commands.add("568k");
                commands.add("-qscale");//视频品质
                commands.add("6");//视频品质参数
                commands.add("-ab");
                commands.add("128k");
                commands.add("-y");//文件存在选择重写
                commands.add(newVideoPath);
                ProcessBuilder builder = new ProcessBuilder();
                builder.command(commands);
                process = builder.start();
                //process.waitFor();//等待进程执行完毕
                //防止ffmpeg进程塞满缓存造成死锁
                InputStream error = process.getErrorStream();
                InputStream is = process.getInputStream();
                byte[] b = new byte[1024];
                int readbytes = -1;
                try {
                    while((readbytes = error.read(b)) != -1){
                        logger.info("FFMPEG视频转换进程错误信息:"+new String(b,0,readbytes));
                    }
                    while((readbytes = is.read(b)) != -1){
                        logger.info("FFMPEG视频转换进程输出内容为:"+new String(b,0,readbytes));
                    }
                }catch (IOException e2){

                }finally {
                    error.close();
                    is.close();
                }
            }
            videoUrl = "http://"+JsonResponseHelper.serverAddress+"/wamei/upload/video/"+videoName;
            logger.info("视频格式转换:"+videoUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return videoUrl;
    }

    private static Integer checkVideoType(String PATH) {
        String type = PATH.substring(PATH.lastIndexOf(".") + 1, PATH.length())
                .toLowerCase();
        // ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
        if (type.equals("avi")) {
            return 0;
        } else if (type.equals("mpg")) {
            return 0;
        } else if (type.equals("wmv")) {
            return 0;
        } else if (type.equals("3gp")) {
            return 0;
        } else if (type.equals("mov")) {
            return 0;
        } else if (type.equals("mp4")) {
            return 9;//本身是MP4格式不用转换
        } else if (type.equals("asf")) {
            return 0;
        } else if (type.equals("asx")) {
            return 0;
        } else if (type.equals("flv")) {
            return 0;
        }
        // 对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等),
        else if (type.equals("wmv9")) {
            return 1;
        } else if (type.equals("rm")) {
            return 1;
        } else if (type.equals("rmvb")) {
            return 1;
        }
        return 9;
    }


    
    
    public static void main(String[] args) {
        //String videoPath = "E:\\wamei_app\\04编码\\java\\idea\\wamei\\out\\artifacts\\wamei_war_exploded\\upload\\video\\20160819\\1471575811202054258.mp4";
        String videoPath = "D:/360Downloads/7049a5246a2d44fe897c2ea1c917eeee.mp4";
        //String filename = "test001.wmv";
        //String url = convertCommand(videoPath,filename);
        //System.out.println("视频链接:"+url);
        for(int i=0;i<10;i++){
            logger.info("线程"+i);
            String fileName = "test00"+i+".wmv";
            VideoConvertUtil v1= new VideoConvertUtil(videoPath,fileName);
            Thread t1 = new Thread(v1);
            t1.start();
        }
    }
}


截取视频封面程序

package com.wamei.util;

import com.wamei.common.ResourceUtils;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 * Created by on 2016/8/23.
 */
public class VideoPreviewImageUtil {
    private final static Logger logger = Logger.getLogger(VideoPreviewImageUtil.class);
    public static final String FFMPEG_PATH = "D:/ffmpeg.exe";
    public static final String serverPath = ResourceUtils.getValue("wamei.server.path");
    private static Process process;

    public static String processImg(String veido_path) {
    	String imageUrl = "";
        File file = new File(veido_path);
        if (!file.exists()) {
            System.err.println("路径[" + veido_path + "]对应的视频文件不存在!");
            return null;
        }
        List<String> commands = new java.util.ArrayList<String>();
        commands.add(FFMPEG_PATH);
        commands.add("-i");
        commands.add(veido_path);
        commands.add("-y");
        commands.add("-f");
        commands.add("image2");
        commands.add("-ss");
        commands.add("8");//这个参数是设置截取视频多少秒时的画面
        //commands.add("-t");
        //commands.add("0.001");
        commands.add("-s");
        commands.add("700x525");
        Long date = new Date().getTime();
        String filePath = "/wamei/upload/image/video_"+date+".jpg";
        imageUrl = serverPath+"/wamei/upload/image/video_"+date+".jpg";
        //imageUrl = serverPath+"/wamei_war_exploded/upload/image/video_"+date+".jpg";//本地测试路径
        System.out.println("图片截取原路径:"+imageUrl);
        commands.add(imageUrl);
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commands);
            process = builder.start();
            process.waitFor();//等待进程执行完毕
            //防止ffmpeg进程塞满缓存造成死锁
            InputStream error = process.getErrorStream();
            InputStream is = process.getInputStream();
            byte[] b = new byte[1024];
            int readbytes = -1;
            try {
                while((readbytes = error.read(b)) != -1){
                    logger.info("FFMPEG截图进程错误信息:"+new String(b,0,readbytes));
                }
                while((readbytes = is.read(b)) != -1){
                    logger.info("FFMPEG截图进程输出内容为:"+new String(b,0,readbytes));
                }
            }catch (IOException e2){

            }finally {
                error.close();
                is.close();
            }
            imageUrl = "http://"+JsonResponseHelper.serverAddress+filePath;
            logger.info("视频封面截取:"+imageUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return imageUrl;
    }

    public static boolean processImg(String veido_path, String ffmpeg_path) {
        File file = new File(veido_path);
        if (!file.exists()) {
            System.err.println("路径[" + veido_path + "]对应的视频文件不存在!");
            return false;
        }
        List<String> commands = new java.util.ArrayList<String>();
        commands.add(ffmpeg_path);
        commands.add("-i");
        commands.add(veido_path);
        commands.add("-y");
        commands.add("-f");
        commands.add("image2");
        commands.add("-ss");
        commands.add("8");//这个参数是设置截取视频多少秒时的画面
        //commands.add("-t");
        //commands.add("0.001");
        commands.add("-s");
        commands.add("700x525");
        String imageUrl = veido_path.substring(0, veido_path.lastIndexOf(".")).replaceFirst("vedio", "file") + ".jpg";
        Long date = new Date().getTime();
        String filePath = "/wamei/upload/image/video_"+date+".jpg";
        imageUrl = serverPath+filePath;
        commands.add(imageUrl);
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commands);
            builder.start();
            System.out.println("截取成功");
            System.out.println("===="+imageUrl);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    
    public static void main(String[] args) {
        String videoPath = "E:\\wamei_app\\04编码\\java\\idea\\wamei\\out\\artifacts\\wamei_war_exploded\\upload\\video\\20160819\\1471575811202054258.mp4";
        processImg(videoPath, VideoPreviewImageUtil.FFMPEG_PATH);
    }
}


处理上传视频处理:

    /**
     * 上传视频
     * @param response
     */
    @RequestMapping(value = "/upload/video",method = RequestMethod.POST)
    @ResponseBody
    public void saveUploadVideo(HttpServletRequest request,HttpServletResponse response) {
        StringBuffer sb = new StringBuffer();
        String savePath="upload/video/";
        String videoName = "",videoType="mp4";
        String picSrc = "",videoLink = "";//视频封面//视频链接
        boolean mark = false,status = false;
        try {
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            MultipartFile file = multipartRequest.getFile("videoFile");
            if (file != null) {
                videoName=file.getOriginalFilename();
                videoType = videoName.substring(videoName.lastIndexOf(".")+1);// 截取视频文件格式
                videoType = StringUtils.isNotEmpty(videoType)?videoType:"mp4";
                String fileName = UUID.randomUUID().toString().replace("-", "")+"."+videoType;
                String path = request.getSession().getServletContext().getRealPath(savePath+fileName);
                videoLink = "http://"+JsonResponseHelper.serverAddress+"/wamei/"+savePath+fileName;
                videoLink = videoLink.substring(0,videoLink.lastIndexOf("."))+".mp4";
                //视频文件大小限制在30M
                if (file.getSize() > 0 && file.getSize() <= 31457280) {
                    logger.info("file size:=================== "+file.getSize());
                    mark = FileHelper.saveUploadedVideo(request,videoName, file, path);
                    if(mark){
                        picSrc = VideoPreviewImageUtil.processImg(path);//截取视频封面
                        if(null!=picSrc){
                            status = true;
                        }
                        //视频格式转换
                        VideoConvertUtil v1= new VideoConvertUtil(path,fileName);
                        Thread t1 = new Thread(v1);
                        t1.start();
                    }
                }

            }
            if(status){
                sb.append("{\"statusCode\":\"1\", \"msg\":\"上传成功!\", \"errorCode\":\"\",\"videoLink\":\""+videoLink+"\", \"videoConver\":\""+picSrc+"\"}");
            }else{
                sb.append("{\"statusCode\":\"0\", \"msg\":\"上传失败\", \"errorCode\":\"\", \"result\":{}}");
            }
        } catch (Exception e) {
            e.printStackTrace();
            sb.append("{\"statusCode\":\"0\", \"msg\":\"上传失败\", \"errorCode\":\"\", \"result\":{}}");
        }
        JsonResponseHelper.renderText(sb.toString(), response);
    }

页面:利用uploadify 插件上传视频

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<head>
<title>挖媒管理后台 - 快讯添加/编辑</title>

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<!-- bootstrap -->
<link href="/wamei/pages/css/bootstrap/bootstrap.css" rel="stylesheet" />
<link href="/wamei/pages/css/bootstrap/bootstrap-datetimepicker.min.css" rel="stylesheet" />
<link href="/wamei/pages/css/bootstrap/bootstrap-responsive.css" rel="stylesheet" />
<link href="/wamei/pages/css/bootstrap/bootstrap-overrides.css" type="text/css"
	rel="stylesheet" />

<!-- global styles -->
<link rel="stylesheet" type="text/css" href="/wamei/pages/css/elements.css" />
<link rel="stylesheet" type="text/css" href="/wamei/pages/css/icons.css" />

<!-- libraries -->
<link rel="stylesheet" type="text/css"
	href="/wamei/pages/css/lib/font-awesome.css" />

<!-- this page specific styles -->
<link rel="stylesheet" href="/wamei/pages/css/compiled/personal-info.css"
	type="text/css" media="screen" />
<!-- this page specific styles -->

<!-- open sans font -->
<link href='/wamei/pages/css/open-sans-font.css' rel='stylesheet' type='text/css' />

<link rel="stylesheet" type="text/css" href="/wamei/pages/js/uploadify/uploadify.css" />

<!--[if lt IE 9]>
      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<style type="text/css">
		#previewDiv{
			margin-left: 134px;
		}
		#previewDiv img{
			margin-right: 6px;
			margin-top:6px;
		}
		#videoFileDiv{
			margin-left: 134px;
		}
		#videoFileDiv a{
			margin-top: 15px;
		}
		#videoDiv{
			margin-left: 134px;
		}
		#videoDiv img{
			margin-right: 6px;
			margin-top:6px;
		}
		.image-remove-icon{
			position: relative;
			top: -36px;
			left: -22px;
			background-color: #3e4ada;
		}
		.image-remove-icon i{
			color:#fff;
		}
		.uploadify{
			height: 50px !important;
		}
		.uploadify-button{
			height: 25px !important;
			line-height: 25px !important;
			width: 70px !important;
			margin-top: 10px;
		}
		.swfupload{
			top: 7px;
			height: 45px;
		}
		.uploadify-queue{
			margin-top: 27px;
		}
	</style>
</head>

<!-- main container .wide-content is used for this layout without sidebar :)  -->
<div class="content wide-content">
	<div class="container-fluid">
		<div class="settings-wrapper" id="pad-wrapper">
			<div class="span7 personal-info">
				<div id="roleEditDiv">
					<h5 class="personal-title">快讯添加/编辑</h5>
				</div>
				<form action="/wamei/newsFlashController/save.htm" method="post" enctype="multipart/form-data" id="editForm">
					<input type="hidden" name="id" value="${mo.id}"/>
					<input type="hidden" name="newsFlashStatus" value="${mo.status}"/>
					<input type="hidden" name="createTime" value="${mo.createTimeStr}"/>

					<div class="field-box">
						<label class="Validform_label">快讯类型:</label>
						<div class="ui-select">
							<select id="newsFlashType" name="newsFlashType.id">
								<option value="">请选择</option>
								<c:forEach items="${typeList}" var="item" varStatus="status">
									<option value="${item.id}">${item.name}</option>
								</c:forEach>
							</select>
						</div>
					</div>

					<div class="field-box">
						<label class="Validform_label">快讯内容:</label>
						<textarea id="content" name="content" value="${mo.content}"  datatype="/^\s*$/|*1-500"  errormsg="快讯内容至少1个字符,最多500个字符!"  style="width: 300px;height: 100px;max-width: 300px;max-height: 100px;">${mo.content}</textarea>
					</div>

					<div class="field-box">
						<label class="Validform_label">快讯图片:</label>
						<div id="previewDiv">
							<img src="/wamei/pages/img/new-gallery-img.png" id="imghead" style='width:100px;height:100px;'/>
						</div>
						<div id="imageFile" style="display:none;">
							<input type="file" name="imagefile" id="hiddenFile" οnchange='previewManyImage(this,"imgFile");'/>
						</div>
					</div>

					<div class="field-box">
						<label class="Validform_label">快讯视频:</label>
						<div id="videoDiv">
						</div>
						<div id="videoFileDiv">
							<input type="file" name="videoFile" id="videoFile"  />
						</div>
					</div>

					<div class="field-box">
						<label class="Validform_label">全文链接:</label>
						<div class="ui-select">
							<select id="linkType" name="linkType" datatype="*" errormsg="请选择全文链接类型!">
								<option value="1">文章ID</option>
								<option value="2" selected>H5链接</option>
							</select>
						</div>
					</div>
					<div class="field-box">
						<label class="Validform_label"></label>
						<input class="span5 inline-input" type="text" value="${mo.link}" id="link" name="link" style="height: 25px"/>
					</div>


					<div class="field-box">
						<label>定时发布:</label>
						<div class="input-append date form_datetime">
							<input class="span5 inline-input" type="text" id="publishTime" readonly name="publishTime" value="${mo.publishTimeStr}" style="height: 25px;width: 135%;" />
							<span class="add-on"><i class="icon-remove"></i></span>
							<span class="add-on"><i class="icon-calendar"></i></span>
						</div>
					</div>

					<div class="span6 field-box actions">
						<input type="button" class="btn-glow primary" value="提交" id="checkButton"/> <span></span>
						<input type="button" class="btn-glow primary" value="取消" οnclick="cancle();" />
					</div>
				</form>
			</div>
		</div>
	</div>
</div>
<!-- end main container -->


<!-- scripts -->
<script src="/wamei/pages/js/jquery.min.js"></script>
<script src="/wamei/pages/js/jquery.form.js"></script>
<script src="/wamei/pages/js/bootstrap.min.js"></script>
<script src="/wamei/pages/js/bootstrap-datetimepicker.js"></script>
<script src="/wamei/pages/js/Validform_v5.3.2.js"></script>
<script type="text/javascript" src="/wamei/pages/js/uploadify/jquery.uploadify.min.js"></script>
<script src="/wamei/pages/js/theme.js"></script>

<script type="text/javascript">

	$(function() {

		$('#videoFile').uploadify({
			'swf'      : '/wamei/pages/js/uploadify/uploadify.swf',
			'uploader' : '/wamei/newsFlashController/upload/video.htm',
			// Your options here
			'formData': {},
			'fileObjName' : 'videoFile',
			'fileSizeLimit' : 0,
			'auto':true,
			'buttonText': '添加视频',
			'fileTypeExts'  : '*.mp4;*.avi;*.3gp;*.wmv;*.flv;*.mov;*.mpeg',
			'removeCompleted' : true,
			'removeTimeout' : 1,
			'multi' : false,
			'onUploadStart' : function(file) {
				var num = $(".videoCover").length;
				if(file.size>=31457280){
					alert("超过视频上传限制大小(30M)!");
					this.cancelUpload(file.id);
				}
				if(null!=num && num>4){
					alert("最多只能上传5个视频!");
					this.cancelUpload(file.id);
					//$('#' + file.id).remove();
				}
			},
			'onUploadSuccess':function(file,json,response){
				//console.log(file);console.log(json);
				var videoId = "video"+Math.floor(Math.random()*100000+1);
				var data = eval("(" + json + ")");
				if (data && data.statusCode == 1) {
					$("#videoDiv").append("<img src='"+data.videoConver+"' link='"+data.videoLink+"' class='videoCover "+videoId+"' οnclick='previewVideo(this);' style='width:100px;height:100px;'/>" +
							"<span class='image-remove-icon icon trash' videoId='"+videoId+"' title='删除' οnclick='videoRemove(this);'><i class='icon-remove'></i></span>");
					var videosData =data.videoConver +"@"+ data.videoLink;
					$("#videoFileDiv").append("<input type='hidden' class='"+videoId+"' name='videos' value='"+videosData+"' />");
				} else {
					alert(data.msg);
				}
			},
			'onQueueComplete' : function(json) {
				$('.uploadify-queue').html('');
				//return false;
			}

		});

        //快讯图片
		var images = '${mo.images}';
		if(images){
			var data = images.split(",");
			console.log(data);
			for(var i=0;i<data.length;i++){
				var imgId = "img"+i;
				$("#imghead").before("<img src='"+data[i]+"' class='imgFile "+imgId+"' style='width:100px;height:100px;'/>" +
						"<span class='image-remove-icon icon trash' imgId='"+imgId+"' title='删除' οnclick='imageRemove(this);'><i class='icon-remove'></i></span>");
				$("#imageFile").append("<input type='hidden' class='"+imgId+"' name='imageUrl' value='"+data[i]+"' />");
			}
		}
        //快讯视频
		var videoCover = '${mo.videoCover}';
		var videos = '${mo.videos}';
		if(videoCover){
			var data = videoCover.split(",");
			var videoData = videos.split(",");
			console.log(data);console.log(videoData);
			for(var i=0;i<data.length;i++){
				var videoId = "video"+i;
				$("#videoDiv").append("<img src='"+data[i]+"' link='"+videoData[i]+"' class='videoCover "+videoId+"' οnclick='previewVideo(this);' style='width:100px;height:100px;'/>" +
						"<span class='image-remove-icon icon trash' videoId='"+videoId+"' title='删除' οnclick='videoRemove(this);'><i class='icon-remove'></i></span>");
				var videosStr = data[i] + "@" +videoData[i];
				$("#videoFileDiv").append("<input type='hidden' class='"+videoId+"' name='videos' value='"+videosStr+"' />");
			}
		}

		$(".form_datetime").datetimepicker({
			format:"yyyy-mm-dd hh:ii:ss",
			autoclose: true,
			todayBtn: true,
			minuteStep: 10,
			pickerPosition: "bottom-left",
			minView:0
		});


		//类型选中
		var typeId = "${mo.newsFlashType.id}";
		if(typeId){
			$("#newsFlashType").val(typeId);
		}

		//全文链接类型
		var linkType = '${mo.linkType}';
		if(linkType) {
			$("#linkType").val(linkType);
		}

		//上传图片
		$("#imghead").on("click", function() {
			$("#hiddenFile").click();
		});

		$("#editForm").Validform({
			btnSubmit:"#checkButton",
			tiptype:function(msg){
				if(msg != '' && msg!='通过信息验证!'){
					alert(msg);
				}
			},
			tipSweep:true,
			beforeSubmit:function(){
				saveForm();
				return false;
			}
		});
	});

function saveForm(){
	var content = $("#content").val();
	var imgageUrl = $("input[name='imageUrl']").val();
	var videoUrl = $("input[name='videos']").val();
	console.log(link+"======m====="+imgageUrl+"====v====="+videoUrl);
	if((!content) && (!videoUrl) && (!imgageUrl)){
		alert("快讯内容,视频,图片三项不能全为空!");
		return ;
	}

	$("#editForm").ajaxSubmit({
        type: 'post',
        async: false,
        success: function($data) {
        	var data = eval("("+$data+")");
			if(data && data.statusCode==1){
				alert("保存信息成功!");
				window.location.href = "/wamei/pages/articles/newsflash_list.jsp";
			}else{
				alert(data.msg);
			}
        }
    });
}
function cancle(){
	window.history.back(-1);
}

//移除图片
function imageRemove(obj){
	var id = $(obj).attr("imgId");
	$("."+id).remove();
	$(obj).remove();
}
//移除视频
function videoRemove(obj){
	var id = $(obj).attr("videoId");
	$("."+id).remove();
	$(obj).remove();
}
//视频播放
function previewVideo(obj){
	var videoUrl = $(obj).attr("link");
	var videoCover = $(obj).attr("src");
	window.open("/wamei/pages/articles/previewVideo.jsp?videoUrl="+videoUrl+"&videoCover="+videoCover+"");
}


</script>


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值