概述:
很多用户使用七牛的云存储服务,存放很多mp4文件到七牛的存储空间,但是通过复制外链,然后在浏览器中播放,经常会遇到“只有音频,没有视频”的情况;
其实这个不是七牛的存储有问题,而是视频的编码方式,浏览器不支持,如:MPEG-4 在googel Chrome 、IE这些浏览器中都是不支持视频播放的,但通过苹果的Safari浏览器是能正常播放的;
思路:
1.上传一个视频到七牛的空间,然后再进行转码操作;
2,上传的同时指定预处理操作,进行转码处理后,上传;
3.查看七牛的avthumb接口说明和支持的编码说明,链接如下:
http://developer.qiniu.com/code/v6/api/dora-api/av/avthumb.html
https://support.qiniu.com/hc/kb/article/182142/?from=draft
第一种方法,代码示例如下:
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;
}