概述:

很多用户使用七牛的云存储服务,存放很多mp4文件到七牛的存储空间,但是通过复制外链,然后在浏览器中播放,经常会遇到“只有音频,没有视频”的情况;

其实这个不是七牛的存储有问题,而是视频的编码方式,浏览器不支持,如:MPEG-4 在googel Chrome 、IE这些浏览器中都是不支持视频播放的,但通过苹果的Safari浏览器是能正常播放的;

思路:

1.上传一个视频到七牛的空间,然后再进行转码操作;

2,上传的同时指定预处理操作,进行转码处理后,上传;

3.查看七牛的avthumb接口说明和支持的编码说明,链接如下:

第一种方法,代码示例如下:

package com.qiniu.dora;

import com.qiniu.base.AccountMgr;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.processing.OperationManager;
import com.qiniu.storage.Configuration;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.qiniu.util.UrlSafeBase64;

/**
* 很多浏览器不支持 MPEG-4编码的视频,所以上传到七牛,用类似google 这样的浏览器是不能正常播放的,只有音频,没有视频
* 所以可以使用七牛的转码接口
*
* @author xuhuanchao
*
*/
public class AVthumbForMpeg4 {

//获取授权对象
Auth auth = Auth.create(AccountMgr.ACCESS_KEY, AccountMgr.SECRET_KEY);
//执行操作的管理对象
OperationManager operationMgr = new OperationManager(auth, new Configuration(Zone.zone0()));
/**
* Test Method
* @param args
*/
public static void main(String[] args) {
new AVthumbForMpeg4().transcoding();
}
/**
* 转码
*/
void transcoding() {
String bucket = "java-bucket"; //存储空间名称
String key = "mpeg_4_type.mp4"; //存储空间中视频的文件名称
String newKey = "H264_type.mp4"; //转码后,另存的文件名称
String pipeline = "admin_merge_radio"; //处理队列

String saveAs = UrlSafeBase64.encodeToString(bucket + ":" + newKey); //saveas接口 参数
String fops = "avthumb/mp4/vcodec/libx264|saveas/" + saveAs; //处理命令 avthumb 和 saveas 通过管道符 | 进行连接

try {
//执行转码和另存 操作
String persistentId = operationMgr.pfop(bucket, key, fops, new StringMap().put("persistentPipeline", pipeline));
System.out.println(persistentId);
} catch (QiniuException e) {
String errorCode = String.valueOf(e.response.statusCode);
System.out.println(errorCode);
e.printStackTrace();
}
}
}


第二种方法,代码示例如下:

import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.qiniu.util.UrlSafeBase64;

import java.io.IOException;

import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.UploadManager;

public class UploadDemo {
//设置好账号的ACCESS_KEY和SECRET_KEY
String ACCESS_KEY = "Access_Key";
String SECRET_KEY = "Secret_Key";
//要上传的空间
String bucketname = "Bucket_Name";
//上传到七牛后保存的文件名
String key = "my-java.png";
//上传文件的路径
String FilePath = "/.../...";

//设置转码操作参数
String fops = "avthumb/mp4/s/640x360/vb/1.25m";
//设置转码的队列
String pipeline = "yourpipelinename";

//可以对转码后的文件进行使用saveas参数自定义命名,当然也可以不指定文件会默认命名并保存在当前空间。
String urlbase64 = UrlSafeBase64.encodeToString("目标Bucket_Name:自定义文件key");
String pfops = fops +"|saveas/"+ urlbase64;

//密钥配置
Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
//创建上传对象
UploadManager uploadManager = new UploadManager();

//上传策略中设置persistentOps字段和persistentPipeline字段
public String getUpToken(){
return auth.uploadToken(bucketname,null,3600,new StringMap()
.putNotEmpty("persistentOps", pfops)
.putNotEmpty("persistentPipeline", pipeline), true);
}

public void upload() throws IOException{
try {
//调用put方法上传
Response res = uploadManager.put(FilePath, null, getUpToken());
//打印返回的信息
System.out.println(res.bodyString());
} catch (QiniuException e) {
Response r = e.response;
// 请求失败时打印的异常的信息
System.out.println(r.toString());
try {
//响应的文本信息
System.out.println(r.bodyString());
} catch (QiniuException e1) {
//ignore
}
}
}

public static void main(String args[]) throws IOException{
new UploadDemo().upload();
}

}


注:上面的Demo只是针对视频转码,如果需要别的功能比如音视频切片、视频截图、视频拼接只需要修改下上面的fops后面的参数就可以了,
eg: fops = "vframe/jpg/offset/1/w/480/h/360/rotate/90"就表示视频截图了。
下面给出一些常见的数据处理功能,可以根据需要进行选择:
//------------------图片缩放-------------------
fops ="imageView/2/w/200/h/200";

//------------------视频转码-------------------
// fops ="avthumb/flv/vb/229k/vcodec/libx264/noDomain/1";

//------------------图片水印-------------------
String pictureurl = UrlSafeBase64.encodeToString("http://developer.qiniu.com/resource/logo-2.jpg");
fops = "watermark/1/image/" + pictureurl;

//------------------视频切片-------------------
fops = "avthumb/m3u8";
//切片与加密参数
fops = "avthumb/m3u8/vb/640k/hlsKey/MDEyMzQ1Njc4OTEyMzQ1Ng==/hlsKeyUrl/aHR0cDovLzd4bGVrYi5jb20yLnowLmdsYi5xaW5pdWNkbi5jb20vcWluaXV0ZXN0LmtleQ==";

//------------------文档转换-------------------
fops = "yifangyun_preview";

//------------------视频截图-------------------
fops = "vframe/jpg/offset/1/w/480/h/360/rotate/90";

//------------------视频拼接-------------------
//拼接视频片段时要保证所有源的画面长宽值一致
//除去作为数据处理对象的源文件以外,还可以指定最多5个源文件(即总计6个片段)
//所有源文件必须属于同一存储空间
//格式:avconcat/<Mode>/format/<Format>/<encodedUrl0>/<encodedUrl1>/<encodedUrl2>/...
String encodedUrl1 = UrlSafeBase64.encodeToString("http://7xl4c9.com1.z0.glb.clouddn.com/pingjie2.flv");
String encodedUrl2 = UrlSafeBase64.encodeToString("http://7xl4c9.com1.z0.glb.clouddn.com/pingjie3.avi");
fops = "avconcat/2/format/mp4/"+encodedUrl1+encodedUrl2;

//------------------多文件压缩-------------------
//可将若干七牛空间中的资源文件,在七牛服务端压缩后存储
//格式:mkzip/<mode>/url/<Base64EncodedURL>/alias/<Base64EncodedAlias>/url/<Base64EncodedURL>
String encodedfile1 = UrlSafeBase64.encodeToString("http://7xl4c9.com1.z0.glb.clouddn.com/photo1.jpg");
String encodedfile2 = UrlSafeBase64.encodeToString("http://7xl4c9.com1.z0.glb.clouddn.com/vedio1.mp4");
String encodedfile3 = UrlSafeBase64.encodeToString("http://7xl4c9.com1.z0.glb.clouddn.com/audio1.mp3");
fops = "mkzip/2/url/"+encodedfile1+"url/"+encodedfile2+"url/"+encodedfile3;


补充说明案例:

页面ajax

 <input type="file" class="form-control" name="file" id="filedata" placeholder="upload"
style="margin-bottom:5px;">
<button onclick="uploadfile(this);">上传视频文件</button>
<input type="hidden" class="form-control" name="videoUrl" id="urlId" placeholder="请输入视频链接"
style="margin-bottom:5px;">
<span id="isfile"></span>
<div id="successAlertFile" class="alert alert-success"
style="display:none;">
<span id="successAlertFile_msg"></span>
</div>


function uploadfile(el) {
var formData = new FormData();
var file = $(el).siblings().filter('#filedata').prop('files')[0];
formData.append("file", $(el).siblings().filter('#filedata').prop('files')[0]);
if(file != null && file != ''){
if (file.size < 104857600) {
$.ajax({
url: '${s.base}/video/upload.html',
type: 'POST',
data: formData,
processData: false, // 不要对data参数进行序列化处理,默认为true
contentType: false, // 不要设置Content-Type请求头,因为文件数据是以 multipart/form-data 来编码
xhr: function () {
myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) {
myXhr.upload.addEventListener('progress', function (e) {
if (e.lengthComputable) {
var percent = Math.floor(e.loaded / e.total * 100);
$(el).siblings().filter('#isfile').html(percent.toString() + '%');
if (percent == 100) {
$(el).siblings().filter('#isfile').html("正在上传:" + "<img src='${s.base}/res/i/loading.gif'/>");
}
}
}, false);
}
return myXhr;
},
success: function (respText) {
var resp = $.parseJSON(respText);
if (resp.errcode == 0) {
console.log(resp,"---success----");
$(el).siblings().filter('#isfile').html('');
$(el).siblings().filter('#successAlertFile').show().fadeOut(3000);//显示模态框
$(el).siblings().filter('#successAlertFile').children().css('color', 'green').html('上传成功!');
$(el).siblings().filter('#urlId').val(resp.data.path);
} else {
$(el).siblings().filter('#successAlertFile').show().fadeOut(3000);
$(el).siblings().filter('#successAlertFile').children().css('color', 'red').html('文件名称重复,请重新上传!');
$(el).siblings().filter('#isfile').html('');
}
},
error: function (res) {
// 请求失败
console.log(res);
$(el).siblings().filter('#successAlertFile').show().fadeOut(3000);
$(el).siblings().filter('#successAlertFile').children().css('color', 'red').html('上传失败,请重新上传!');
$(el).siblings().filter('#isfile').html('');
}
});
} else {
$(el).siblings().filter('#successAlertFile').show().fadeOut(3000);
$(el).siblings().filter('#successAlertFile').children().css('color', 'red').html('上传视频不能大于100MB!');
}
} else {
$(el).siblings().filter('#successAlertFile').show().fadeOut(3000);
$(el).siblings().filter('#successAlertFile').children().css('color', 'red').html('上传视频不能为空!');
}
/* var demo = $(el).siblings().filter('#urlId').val()
console.log(demo,"----demo---")*/
}


controller

  package com.online.college.opt.controller;

import com.online.college.common.storage.QiniuStorage;
import com.online.college.common.web.JsonView;
import com.online.college.core.consts.domain.CollegeClassTeacher;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
* @Author: LBX
* @Date: 2018/12/24 14:07
*/
@Controller
@RequestMapping("/video")
public class UploadFileController {

@RequestMapping("/upload")
@ResponseBody
public String upload(@RequestParam("file") CommonsMultipartFile picture){
Map<String,Object> map = new HashMap<>();
if (null != picture && picture.getBytes().length > 0) {
String key = QiniuStorage.uploadVideo(picture);
map.put("key",key);
}
return JsonView.render(map);
}
}


service

   /**
* 上传视频
* @param picture
*/
public static String uploadVideo(CommonsMultipartFile picture){
String key = QiniuKeyGenerator.generateKey();
key = QiniuWrapper.uploadVideo(picture, key);
return key;
}


serviceimpl

  /**
* 上传视频
*
* @return
*/
public static String uploadVideo(CommonsMultipartFile picture, String key) {
DiskFileItem diskFileItem = (DiskFileItem) picture.getFileItem();
File file = diskFileItem.getStoreLocation();
try {
String fileName = picture.getOriginalFilename();
//设置转码操作参数
String fops = "avthumb/mp4/vcodec/libx264";
//可以对转码后的文件进行使用saveas参数自定义命名,当然也可以不指定文件会默认命名并保存在当前空间。
String urlbase64 = UrlSafeBase64.encodeToString(bucketName + ":" + fileName + ".mp4");
String pfops = fops + "|saveas/" + urlbase64;
String upToken = auth.uploadToken(bucketName, null, 3600, new StringMap().putNotEmpty("persistentOps", pfops));
key = key + ".mp4";
//调用put方法上传
Response response = uploadManager.put(file, key, upToken);
//打印返回的信息
System.out.println("-----success---" + response.bodyString());
DefaultPutRet ret = response.jsonToObject(DefaultPutRet.class);
return ret.key;
} catch (QiniuException e) {
logger.error("upload file to qiniu cloud storage failed", e);
}
return null;
}