Просмотр исходного кода

fix video

release_toaliyun_real
xiaohanzi 5 лет назад
Родитель
Сommit
2ad3875a79
18 измененных файлов: 436 добавлений и 993 удалений
  1. +1
    -1
      mallinkAdmin/src/main/java/com/iformall/config/ShiroConfig.java
  2. +22
    -9
      mallinkAdmin/src/main/java/com/iformall/controller/video/VideoController.java
  3. +5
    -0
      mallinkAdmin/src/main/resources/application-dev.yml
  4. +2
    -2
      mallinkAdmin/src/main/resources/application.yml
  5. +30
    -43
      mallinkVideo/pom.xml
  6. +3
    -6
      mallinkVideo/src/main/java/com/iformall/video/VideoExcutor.java
  7. +43
    -64
      mallinkVideo/src/main/java/com/iformall/video/aliyun/AliyunVideoExcutor.java
  8. +1
    -0
      mallinkVideo/src/main/java/com/iformall/video/aliyun/config/AliyunVideoConfig.java
  9. +82
    -0
      mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/server/AliyunVedioServer.java
  10. +16
    -0
      mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/server/OssUploadClient.java
  11. +90
    -0
      mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/server/PutObjectProgressListener.java
  12. +59
    -0
      mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/server/UploadCacheHelper.java
  13. +0
    -695
      mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/upload/AliyunVedioUpload.java
  14. +0
    -131
      mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/upload/PutObjectProgressListener.java
  15. +0
    -37
      mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/upload/UploadCacheHelper.java
  16. +50
    -0
      mallinkVideo/src/main/java/com/iformall/video/aliyun/thread/AliyunVedioThreadExecutorConfig.java
  17. +32
    -0
      mallinkVideo/src/main/java/com/iformall/video/aliyun/thread/AliyunVideoUploadThread.java
  18. +0
    -5
      pom.xml

+ 1
- 1
mallinkAdmin/src/main/java/com/iformall/config/ShiroConfig.java Просмотреть файл

@@ -154,7 +154,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/logout", "authc");

filterChainDefinitionMap.put("/**", "corsFilter,token,authc");
// filterChainDefinitionMap.put("/**", "anon");
filterChainDefinitionMap.put("/**", "anon");


shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);


+ 22
- 9
mallinkAdmin/src/main/java/com/iformall/controller/video/VideoController.java Просмотреть файл

@@ -73,14 +73,6 @@ public class VideoController extends BaseController {
@Autowired
String videoType;

@GetMapping("/test")
@ApiImplicitParams({
@ApiImplicitParam(name = "title", value = "页数", dataType = "String", paramType = "query", required = true)})
public ResultData list(@ModelAttribute WxPropertyContract wxPropertyContract, String title) {
VideUploadResult result = videoFactory.getExcutor(videoType).uploadLocalVideo("123123213", "C:\\Users\\xiaohu\\Desktop\\111.mp4");
return new ResultData(result);
}
/**
* 上传视频
*
@@ -104,7 +96,7 @@ public class VideoController extends BaseController {
if(!fileFormat.endsWith("mp4")){
return new ResultData(ErrorCode.PICTURE_ENDWIDTH_ERROR);
}
VideUploadResult result = videoFactory.getExcutor(videoType).uploadLocalVideo(title, multiReq.getInputStream(),fileFormat);
VideUploadResult result = videoFactory.getExcutor(videoType).uploadVideoStream(title, multiReq.getInputStream(),fileFormat);
return new ResultData(result);
} catch (Exception e) {
logger.error(e.getMessage());
@@ -132,5 +124,26 @@ public class VideoController extends BaseController {
return new ResultData(ErrorCode.PICTURE_ANALYZING_ERROR);
}
}
/**
* 上传视频
*
* @param multiReq
* @return
* @throws Exception
*/
@GetMapping(value = "/videoContentLength")
@ApiOperation("上传视频进度")
@ApiImplicitParams({
@ApiImplicitParam(name = "videoId", value = "视频编号", dataType = "String", paramType = "query", required = true)})
public ResultData videoContentLength(String videoId) {
try {
String result = videoFactory.getExcutor(videoType).getVedioContentLength(videoId);
return new ResultData(result);
} catch (Exception e) {
logger.error(e.getMessage());
return new ResultData(ErrorCode.PICTURE_ANALYZING_ERROR);
}
}

}

+ 5
- 0
mallinkAdmin/src/main/resources/application-dev.yml Просмотреть файл

@@ -172,6 +172,11 @@ video:
accessKeyId: LTAI4G7ixY4AhvM35F8o3W3V
accessKeySecret: VfWqGb83qIQrS9us45utskl8itd7ry
regionId: cn-beijing
endPoint: https://oss-cn-beijing.aliyuncs.com
corePoolSize: 6
maxPoolSize: 20
queueCapacity: 1000
namePrefix: aliyun-video-upload


jasypt:


+ 2
- 2
mallinkAdmin/src/main/resources/application.yml Просмотреть файл

@@ -14,8 +14,8 @@ spring:
default-property-inclusion: non_null
servlet:
multipart:
max-file-size: 20MB
max-request-size: 200MB
max-file-size: 300MB
max-request-size: 1024MB
cache:
type: REDIS
cache-names: redis_cache #缓存的名字(可以不指定)


+ 30
- 43
mallinkVideo/pom.xml Просмотреть файл

@@ -8,50 +8,37 @@
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>mallinkVideo</artifactId>

<repositories>
<repository>
<id>sonatype-nexus-staging</id>
<name>Sonatype Nexus Staging</name>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-vod</artifactId>
<version>2.15.11</version>
</dependency>
<!-- 引入外包 -->
<!-- mvn install:install-file -Dfile=/lib/aliyun-java-vod-upload-1.4.14.jar -DgroupId=com.aliyun -DartifactId=aliyun-java-vod-upload -Dversion=1.4.14 -Dpackaging=jar -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-vod-upload</artifactId>
<version>1.4.14</version>
<!-- <scope>system</scope>
<systemPath>${basedir}/src/main/resources/lib/aliyun-java-vod-upload-1.4.14.jar</systemPath> -->
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20170516</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-vod</artifactId>
<version>2.15.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
</project>

+ 3
- 6
mallinkVideo/src/main/java/com/iformall/video/VideoExcutor.java Просмотреть файл

@@ -11,14 +11,11 @@ public interface VideoExcutor {
* 上传视频
* title:视频标题
*/
public VideUploadResult uploadLocalVideo(String title,String localFile);
public VideUploadResult uploadLocalVideo(String title,InputStream inputStream,String fileFormat);
//ext,后缀名
public VideUploadResult uploadNetVideo(String title,String url,String ext);
public VideUploadResult uploadVideoStream(String title,InputStream inputStream,String ex);
public String getVedioUploadProgress(String videoId);
public String getVedioContentLength(String videoId);

VideUploadResult getVideoDetail(String videoId);
}

+ 43
- 64
mallinkVideo/src/main/java/com/iformall/video/aliyun/AliyunVideoExcutor.java Просмотреть файл

@@ -1,26 +1,28 @@
package com.iformall.video.aliyun;

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

import com.aliyuncs.vod.model.v20170321.CreateUploadVideoResponse;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoResponse;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;

import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.aliyun.vod.upload.resp.UploadStreamResponse;
import com.aliyun.vod.upload.resp.UploadURLStreamResponse;
import com.aliyun.vod.upload.resp.UploadVideoResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.iformall.video.VideoExcutor;
import com.iformall.video.aliyun.config.AliyunVideoConfig;
import com.iformall.video.aliyun.sdk.server.AliyunVedioServer;
import com.iformall.video.aliyun.sdk.upload.AliyunVedioUpload;
import com.iformall.video.aliyun.sdk.server.UploadCacheHelper;
import com.iformall.video.aliyun.thread.AliyunVideoUploadThread;
import com.iformall.video.entity.VideUploadResult;

import lombok.extern.slf4j.Slf4j;

@Component
@@ -30,6 +32,9 @@ public class AliyunVideoExcutor implements VideoExcutor {
@Autowired
AliyunVideoConfig config;
@Autowired
AliyunVideoUploadThread thread;
@Autowired
@Qualifier("objectCommonRedisTemplate")
RedisTemplate<String, Object> redisTemplate;
@@ -43,20 +48,35 @@ public class AliyunVideoExcutor implements VideoExcutor {
return null;
}
public DefaultAcsClient initVodClient() throws ClientException {
String regionId = config.getRegionId(); // 点播服务接入区域
DefaultProfile profile = DefaultProfile.getProfile(regionId, config.getAccessKeyId(), config.getAccessKeySecret());
DefaultAcsClient client = new DefaultAcsClient(profile);
return client;
}
/**
* https://help.aliyun.com/document_detail/61063.html?spm=a2c4g.11186623.6.1036.2f81d418ydqsX8
* https://help.aliyun.com/document_detail/55407.htm?spm=a2c4g.11186623.2.12.77aad4180Hg9y0#doc-api-vod-CreateUploadVideo
*/
@Override
public VideUploadResult uploadLocalVideo(String title,String localFile) {
public VideUploadResult uploadVideoStream(String title,InputStream inputStream,String ex) {
VideUploadResult result = new VideUploadResult();
try {
UploadVideoResponse response =AliyunVedioUpload.uploadVideo(config.getAccessKeyId(), config.getAccessKeySecret(),config.getRegionId(), title, localFile,redisTemplate);
if (response.isSuccess()) {
result.setVideoId(response.getVideoId());
result.setVideoUrl(getUrlByVeidoId(response.getVideoId()));
result.setSuccess(true);
}else {
//如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因
result.setSuccess(false);
result.setMsg("ErrorCode["+response.getCode()+"]"+response.getMessage());
//该接口不会真正上传视频文件,您需要获得上传地址和凭证后,对其进行Base64解码,再调用OSS SDK将视频文件上传至指定的bucket中。详细操作,请参见基于OSS原生SDK上传视频到点播
File folder = new File ("/video/upload/temp/aliyun") ;
if (!folder.exists()) {
folder.mkdirs();
}
String _tempfilepath = "/video/upload/temp/aliyun/"+IdWorker.get32UUID()+"."+ex;
File targetFile = new File(_tempfilepath);
FileUtils.copyInputStreamToFile(inputStream, targetFile);
CreateUploadVideoResponse response =AliyunVedioServer.createUploadVideo(config, title, _tempfilepath);
result.setVideoId(response.getVideoId());
//result.setVideoUrl(getUrlByVeidoId(response.getVideoId()));
result.setSuccess(true);
//TODO 线程池的方式
thread.executeAysncTask(response, _tempfilepath,redisTemplate);
} catch (Exception e) {
log.error("aliyun uploadLocalVideo error",e);
result.setSuccess(false);
@@ -82,55 +102,9 @@ public class AliyunVideoExcutor implements VideoExcutor {
}
}

@Override
public VideUploadResult uploadLocalVideo(String title,InputStream inputStream,String fileFormat) {
VideUploadResult result = new VideUploadResult();
UploadStreamResponse response;
try {
response = AliyunVedioUpload.uploadStream(config.getAccessKeyId(), config.getAccessKeySecret(),config.getRegionId(), title, UUID.randomUUID().toString().replace("-","")+fileFormat, inputStream,redisTemplate);
if (response.isSuccess()) {
result.setVideoId(response.getVideoId());
// setVideUploadResult(result,response.getVideoId());
result.setVideoUrl(getUrlByVeidoId(response.getVideoId()));
result.setSuccess(true);
} else { //如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因
result.setSuccess(false);
result.setMsg("ErrorCode["+response.getCode()+"]"+response.getMessage());
}
} catch (Exception e) {
log.error("aliyun uploadLocalVideo error",e);
result.setSuccess(false);
result.setMsg(e.getLocalizedMessage());
}finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
return result;
}

@Override
public VideUploadResult uploadNetVideo(String title,String url,String ext) {
VideUploadResult result = new VideUploadResult();
UploadURLStreamResponse response = AliyunVedioUpload.uploadURLStream(config.getAccessKeyId(), config.getAccessKeySecret(),config.getRegionId(), title, url, ext,redisTemplate);
if (response.isSuccess()) {
result.setVideoId(response.getVideoId());
result.setVideoUrl(getUrlByVeidoId(response.getVideoId()));
result.setSuccess(true);
} else {
/* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */
result.setSuccess(false);
result.setMsg("ErrorCode["+response.getCode()+"]"+response.getMessage());
}
return result;
}

@Override
public String getVedioUploadProgress(String videoId) {
return AliyunVedioUpload.getUploadProgress(redisTemplate, videoId);
return UploadCacheHelper.getUploadProgress(redisTemplate, videoId);
}

@Override
@@ -161,4 +135,9 @@ public class AliyunVideoExcutor implements VideoExcutor {
}
return result;
}

@Override
public String getVedioContentLength(String videoId) {
return UploadCacheHelper.getContentLength(redisTemplate, videoId);
}
}

+ 1
- 0
mallinkVideo/src/main/java/com/iformall/video/aliyun/config/AliyunVideoConfig.java Просмотреть файл

@@ -12,4 +12,5 @@ public class AliyunVideoConfig {
private String accessKeyId;
private String accessKeySecret;
private String regionId;//cn-shanghai 点播服务接入区域
private String endPoint;//以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com
}

+ 82
- 0
mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/server/AliyunVedioServer.java Просмотреть файл

@@ -1,9 +1,26 @@
package com.iformall.video.aliyun.sdk.server;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Base64;
import java.util.List;

import org.springframework.data.redis.core.RedisTemplate;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.vod.model.v20170321.CreateUploadVideoRequest;
import com.aliyuncs.vod.model.v20170321.CreateUploadVideoResponse;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoRequest;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoResponse;
import com.iformall.video.aliyun.config.AliyunVideoConfig;
@@ -22,6 +39,71 @@ public class AliyunVedioServer {
return client;
}
/**
* 获取视频上传地址和凭证
* @param client 发送请求客户端
* @return CreateUploadVideoResponse 获取视频上传地址和凭证响应数据
* @throws Exception
*/
public static CreateUploadVideoResponse createUploadVideo(AliyunVideoConfig config,String title,String fileName) throws Exception {
DefaultAcsClient client = initVodClient(config);
CreateUploadVideoRequest request = new CreateUploadVideoRequest();
request.setTitle(title);
request.setFileName(fileName);
//UserData,用户自定义设置参数,用户需要单独回调URL及数据透传时设置(非必须)
//JSONObject userData = new JSONObject();

//UserData回调部分设置
//JSONObject messageCallback = new JSONObject();
//messageCallback.put("CallbackURL", "http://192.168.0.0/16");
//messageCallback.put("CallbackType", "http");
//userData.put("MessageCallback", messageCallback.toJSONString());

//UserData透传数据部分设置
//JSONObject extend = new JSONObject();
//extend.put("MyId", "user-defined-id");
//userData.put("Extend", extend.toJSONString());

//request.setUserData(userData.toJSONString());

return client.getAcsResponse(request);
}
private static OssUploadClient initOssClient(CreateUploadVideoResponse response) throws UnsupportedEncodingException {
final Base64.Decoder decoder = Base64.getDecoder();
String uploadAddressStr = new String(decoder.decode(response.getUploadAddress().getBytes("UTF-8")));
String uploadAuthStr = new String(decoder.decode(response.getUploadAuth().getBytes("UTF-8")));
JSONObject uploadAuth = JSON.parseObject(uploadAuthStr);
JSONObject uploadAddress = JSON.parseObject(uploadAddressStr);
String bucket = uploadAddress.getString("Bucket");
String endpoint = uploadAddress.getString("Endpoint");
String fileName = uploadAddress.getString("FileName");
String accessKeyId = uploadAuth.getString("AccessKeyId");
String accessKeySecret = uploadAuth.getString("AccessKeySecret");
String securityToken = uploadAuth.getString("SecurityToken");
OSS client = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
return new OssUploadClient(bucket, endpoint, fileName, client);
}
public static void uploadVideo(CreateUploadVideoResponse response,String localFile,RedisTemplate<String, Object> redisTemplate) throws UnsupportedEncodingException {
OssUploadClient ossClient = initOssClient(response);
// 创建PutObjectRequest对象。
// 填写Bucket名称、Object完整路径和本地文件的完整路径。Object完整路径中不能包含Bucket名称。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
//PutObjectRequest putObjectRequest = new PutObjectRequest(ossClient.getBucket(), ossClient.getFileName(), new File(localFile));

// 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
// ObjectMetadata metadata = new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// metadata.setObjectAcl(CannedAccessControlList.Private);
// putObjectRequest.setMetadata(metadata);
// 上传文件。
ossClient.getOssClient().putObject(new PutObjectRequest(ossClient.getBucket(), ossClient.getFileName(), new File(localFile)).
<PutObjectRequest>withProgressListener(new PutObjectProgressListener(redisTemplate,response.getVideoId(),localFile)));
// 关闭OSSClient。
ossClient.getOssClient().shutdown();
}
//获取视频播放地址
//https://help.aliyun.com/document_detail/61064.html?spm=a2c4g.11186623.6.1043.7c2d3838K6wQPZ
public static String getVedioUrl(AliyunVideoConfig config,String videoId) throws ClientException {


+ 16
- 0
mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/server/OssUploadClient.java Просмотреть файл

@@ -0,0 +1,16 @@
package com.iformall.video.aliyun.sdk.server;

import com.aliyun.oss.OSS;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class OssUploadClient {

private String bucket;
private String endpoint;
private String fileName;
private OSS ossClient;
}

+ 90
- 0
mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/server/PutObjectProgressListener.java Просмотреть файл

@@ -0,0 +1,90 @@
package com.iformall.video.aliyun.sdk.server;

import java.io.File;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;

import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;

public class PutObjectProgressListener implements ProgressListener{
private String videoId = "";
private String localFile = "";
private long bytesWritten = 0;
private long totalBytes = -1;
private boolean succeed = false;
private RedisTemplate<String, Object> redisTemplate;
public PutObjectProgressListener(RedisTemplate<String, Object> redisTemplate,String videoId,String localFile) {
this.redisTemplate = redisTemplate;
this.videoId = videoId;
this.localFile = localFile;
}

@Override
public void progressChanged(ProgressEvent progressEvent) {
long bytes = progressEvent.getBytes();
ProgressEventType eventType = progressEvent.getEventType();
switch (eventType) {
case TRANSFER_STARTED_EVENT:
if (null != redisTemplate && (!StringUtils.isBlank(videoId))) {
UploadCacheHelper.cacheStart(redisTemplate, videoId);
}
//System.out.println("Start to upload......");
break;
case REQUEST_CONTENT_LENGTH_EVENT:
this.totalBytes = bytes;
if (null != redisTemplate && (!StringUtils.isBlank(videoId))) {
UploadCacheHelper.setContentLength(redisTemplate, videoId, String.valueOf(this.totalBytes));
}
//System.out.println(this.totalBytes + " bytes in total will be uploaded to OSS");
break;
case REQUEST_BYTE_TRANSFER_EVENT:
this.bytesWritten += bytes;
if (this.totalBytes != -1) {
int percent = (int)(this.bytesWritten * 100.0 / this.totalBytes);
//System.out.println(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
} else {
//System.out.println(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
}
if (null != redisTemplate && (!StringUtils.isBlank(videoId))) {
UploadCacheHelper.cacheProgress(redisTemplate, videoId, String.valueOf(this.bytesWritten));
}
break;
case TRANSFER_COMPLETED_EVENT:
this.succeed = true;
if (null != redisTemplate && (!StringUtils.isBlank(videoId))) {
UploadCacheHelper.cacheComplete(redisTemplate, videoId);
}
if (!StringUtils.isBlank(localFile)) {
File f = new File(localFile);
if (f.exists()) {
f.delete();
}
}
//System.out.println("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
break;
case TRANSFER_FAILED_EVENT:
//System.out.println("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
if (null != redisTemplate && (!StringUtils.isBlank(videoId))) {
UploadCacheHelper.cacheError(redisTemplate, videoId);
}
if (!StringUtils.isBlank(localFile)) {
File f = new File(localFile);
if (f.exists()) {
f.delete();
}
}
break;
default:
break;
}
}

public boolean isSucceed() {
return succeed;
}
}

+ 59
- 0
mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/server/UploadCacheHelper.java Просмотреть файл

@@ -0,0 +1,59 @@
package com.iformall.video.aliyun.sdk.server;

import java.util.concurrent.TimeUnit;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

public class UploadCacheHelper {

private static String PRE = "video:upload:aliyun:progress:";
public static void cacheStart(RedisTemplate<String, Object> template,String videoId) {
ValueOperations<String, Object> operations = template.opsForValue();
operations.set(PRE+videoId, "begin", 24*3600,TimeUnit.SECONDS);
}
public static void cacheComplete(RedisTemplate<String, Object> template,String videoId) {
ValueOperations<String, Object> operations = template.opsForValue();
operations.set(PRE+videoId, "complete", 24*3600,TimeUnit.SECONDS);
}
public static void cacheProgress(RedisTemplate<String, Object> template,String videoId,String bytes) {
ValueOperations<String, Object> operations = template.opsForValue();
operations.set(PRE+videoId, bytes, 24*3600,TimeUnit.SECONDS);
}
public static void cacheError(RedisTemplate<String, Object> template,String videoId) {
ValueOperations<String, Object> operations = template.opsForValue();
operations.set(PRE+videoId, "error", 24*3600,TimeUnit.SECONDS);
}
public static String getUploadProgress(RedisTemplate<String, Object> template,String videoId) {
if (template.hasKey(PRE+videoId)) {
ValueOperations<String, Object> operations = template.opsForValue();
Object o = operations.get(PRE+videoId);
if (o instanceof String) {
return (String)o;
}
}
return null;
}
public static void setContentLength(RedisTemplate<String, Object> template,String videoId,String length) {
ValueOperations<String, Object> operations = template.opsForValue();
operations.set("video:upload:aliyun:contengLength:"+videoId, length, 24*3600,TimeUnit.SECONDS);
}
public static String getContentLength(RedisTemplate<String, Object> template,String videoId) {
if (template.hasKey("video:upload:aliyun:contengLength:"+videoId)) {
ValueOperations<String, Object> operations = template.opsForValue();
Object o = operations.get("video:upload:aliyun:contengLength:"+videoId);
if (o instanceof String) {
return (String)o;
}
}
return null;
}
}

+ 0
- 695
mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/upload/AliyunVedioUpload.java Просмотреть файл

@@ -1,695 +0,0 @@
package com.iformall.video.aliyun.sdk.upload;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import org.springframework.data.redis.core.RedisTemplate;

import com.aliyun.vod.upload.impl.UploadAttachedMediaImpl;
import com.aliyun.vod.upload.impl.UploadImageImpl;
import com.aliyun.vod.upload.impl.UploadM3u8FileImpl;
import com.aliyun.vod.upload.impl.UploadVideoImpl;
import com.aliyun.vod.upload.req.UploadAttachedMediaRequest;
import com.aliyun.vod.upload.req.UploadFileStreamRequest;
import com.aliyun.vod.upload.req.UploadImageRequest;
import com.aliyun.vod.upload.req.UploadLocalM3u8Request;
import com.aliyun.vod.upload.req.UploadStreamRequest;
import com.aliyun.vod.upload.req.UploadURLStreamRequest;
import com.aliyun.vod.upload.req.UploadVideoRequest;
import com.aliyun.vod.upload.req.UploadWebM3u8Request;
import com.aliyun.vod.upload.resp.UploadAttachedMediaResponse;
import com.aliyun.vod.upload.resp.UploadFileStreamResponse;
import com.aliyun.vod.upload.resp.UploadImageResponse;
import com.aliyun.vod.upload.resp.UploadLocalM3u8Response;
import com.aliyun.vod.upload.resp.UploadStreamResponse;
import com.aliyun.vod.upload.resp.UploadURLStreamResponse;
import com.aliyun.vod.upload.resp.UploadVideoResponse;
import com.aliyun.vod.upload.resp.UploadWebM3u8Response;

import lombok.extern.slf4j.Slf4j;
/**
* 以下Java示例代码演示了如何在服务端上传媒资文件至视频点播,媒资类型支持音频、视频和图片。
* <p>
* 一、音视频上传目前支持4种方式上传:
*
* 1.上传本地文件,使用分片上传,并支持断点续传,参见testUploadVideo函数。
* 1.1 当断点续传关闭时,最大支持上传任务执行时间为3000秒,具体可上传文件大小与您的网络带宽及磁盘读写能力有关。
* 1.2 当断点续传开启时,最大支持48.8TB的单个文件,注意,断点续传开启后,上传任务执行过程中,同时会将当前上传位置写入本地磁盘文件,影响您上传文件的速度,请您根据文件大小选择是否开启
*
* 2.上传网络流,可指定文件URL进行上传,支持断点续传,最大支持48.8TB的单个文件。
* 该上传方式需要先将网络文件下载到本地磁盘,再进行上传,所以要保证本地磁盘有充足的空间。参见testUploadURLStream函数。
*
* 3.上传文件流,可指定本地文件进行上传,不支持断点续传,最大支持5GB的单个文件。参见testUploadFileStream函数。
*
* 4.流式上传,可指定输入流进行上传,支持文件流和网络流等,不支持断点续传,最大支持5GB的单个文件。参见testUploadStream函数。
* <p>
*
* 二、图片上传目前支持2种方式上传:
* 1.上传本地文件,不支持断点续传,最大支持5GB的单个文件,参见testUploadImageLocalFile函数
* 2.上传文件流和网络流,InputStream参数必选,不支持断点续传,最大支持5GB的单个文件。参见testUploadImageStream函数。
* 注:图片上传完成后,会返回图片ID和图片地址,也可通过GetImageInfo查询图片信息,参见接口文档 https://help.aliyun.com/document_detail/89742.html
* <p>
*
* 三、m3u8文件上传目前支持2种方式:
* 1.上传本地m3u8音视频文件(包括所有分片文件)到点播,需指定本地m3u8索引文件地址和所有分片地址。
* 2.上传网络m3u8音视频文件(包括所有分片文件)到点播,需指定m3u8索引文件和分片文件的URL地址。
*
* 注:
* 1) 上传网络m3u8音视频文件时需要保证地址可访问,如果有权限限制,请设置带签名信息的地址,且保证足够长的有效期,防止地址无法访问导致上传失败
* 2) m3u8文件上传暂不支持进度回调
* <p>
*
* 四、上传进度回调通知:
* 1.默认上传进度回调函数:视频点播上传SDK内部默认开启上传进度回调函数,输出不同事件通知的日志,您可以设置关闭该上传进度通知及日志输出;
* 2.自定义上传进度回调函数:您可根据自已的业务场景重新定义不同事件处理的方式,只需要修改上传回调示例函数即可。
* <p>
*
* 五、辅助媒资上传目前支持2种方式:
* 1.上传本地文件,不支持断点续传,最大支持5GB的单个文件,参见testUploadAttachedMediaLocalFile函数
* 2.上传文件流和网络流,InputStream参数必选,不支持断点续传,最大支持5GB的单个文件。参见testUploadAttachedMediaStream函数。
* <p>
*
* 六、支持STS方式上传:
* 1.您需要实现VoDRefreshSTSTokenListener接口的onRefreshSTSToken方法,用于生成STS信息,
* 当文件上传时间超过STS过期时间时,SDK内部会定期调用此方法刷新您的STS信息进行后续文件的上传。
* <p>
*
* 七、可指定上传脚本部署的ECS区域(设置Request的EcsRegionId参数,取值参考存储区域标识:https://help.aliyun.com/document_detail/98194.html),
* 如果与点播存储(OSS)区域相同,则自动使用内网上传文件至存储,上传更快且更省公网流量
* 由于点播API只提供外网域名访问,因此部署上传脚本的ECS服务器必须具有访问外网的权限。
*
* 注意:
* 请替换示例中的必选参数,示例中的可选参数如果您不需要设置,请将其删除,以免设置无效参数值与您的预期不符。
*/

@Slf4j
public class AliyunVedioUpload {
public static void main(String[] args) {
//账号AK信息请填写(必选)
String accessKeyId = "<Your accessKeyId>";
//账号AK信息请填写(必选)
String accessKeySecret = "<Your accessKeySecret>";
// 一、视频文件上传
// 视频标题(必选)
String title = "测试标题";
// 1.本地文件上传和文件流上传时,文件名称为上传文件绝对路径,如:/User/sample/文件名称.mp4 (必选)
// 2.网络流上传时,文件名称为源文件名,如文件名称.mp4(必选)。
// 3.流式上传时,文件名称为源文件名,如文件名称.mp4(必选)。
// 任何上传方式文件名必须包含扩展名
String fileName = "/Users/test/video/test.mp4";
// 本地文件上传
uploadVideo(accessKeyId, accessKeySecret,"cn-beijing", title, fileName,null);

// 待上传视频的网络流地址
String url = "http://test.aliyun.com/video/test.mp4";

// 2.网络流上传
// 文件扩展名,当url中不包含扩展名时,需要设置该参数
String fileExtension = "mp4";
uploadURLStream(accessKeyId, accessKeySecret, "cn-beijing",title, url, fileExtension,null);

// 3.文件流上传
// testUploadFileStream(accessKeyId, accessKeySecret, title, fileName);

// 4.流式上传,如文件流和网络流
InputStream inputStream = null;
// 4.1 文件流
try {
inputStream = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 4.2 网络流
try {
inputStream = new URL(url).openStream();
} catch (IOException e) {
e.printStackTrace();
}
uploadStream(accessKeyId, accessKeySecret, "cn-beijing",title, fileName, inputStream,null);

// 二、图片上传

// 1.图片上传-本地文件上传
// testUploadImageLocalFile(accessKeyId, accessKeySecret);

// 2.图片上传-流式上传(文件流和网络流)
// testUploadImageStream(accessKeyId, accessKeySecret);

// 三、m3u8文件上传

// 1.上传本地m3u8音视频文件
uploadLocalM3u8(accessKeyId, accessKeySecret,"cn-beijing");
// 2.上传网络m3u8音视频文件
uploadWebM3u8(accessKeyId, accessKeySecret,"cn-beijing");
// 四、辅助媒资上传
// 辅助媒资-本地文件上传
// testUploadAttachedMediaLocalFile(accessKeyId, accessKeySecret);
// 辅助媒资-流式上传(文件流和网络流)
// testUploadAttachedMediaStream(accessKeyId, accessKeySecret);
}

/**
* 本地文件上传接口
*
* @param accessKeyId
* @param accessKeySecret
* @param title
* @param fileName
*/
public static UploadVideoResponse uploadVideo(String accessKeyId, String accessKeySecret,String regionId, String title, String fileName,
RedisTemplate<String, Object> redisTemplate) {
UploadVideoRequest request = new UploadVideoRequest(accessKeyId, accessKeySecret, title, fileName);
/* 可指定分片上传时每个分片的大小,默认为2M字节 */
request.setPartSize(2 * 1024 * 1024L);
/* 可指定分片上传时的并发线程数,默认为1,(注:该配置会占用服务器CPU资源,需根据服务器情况指定)*/
request.setTaskNum(1);
/* 是否开启断点续传, 默认断点续传功能关闭。当网络不稳定或者程序崩溃时,再次发起相同上传请求,可以继续未完成的上传任务,适用于超时3000秒仍不能上传完成的大文件。
注意: 断点续传开启后,会在上传过程中将上传位置写入本地磁盘文件,影响文件上传速度,请您根据实际情况选择是否开启*/
//request.setEnableCheckpoint(false);
/* OSS慢请求日志打印超时时间,是指每个分片上传时间超过该阈值时会打印debug日志,如果想屏蔽此日志,请调整该阈值。单位: 毫秒,默认为300000毫秒*/
//request.setSlowRequestsThreshold(300000L);
/* 可指定每个分片慢请求时打印日志的时间阈值,默认为300s*/
//request.setSlowRequestsThreshold(300000L);
/* 是否显示水印(可选),指定模板组ID时,根据模板组配置确定是否显示水印*/
//request.setIsShowWaterMark(true);
/* 自定义消息回调设置(可选),参数说明参考文档 https://help.aliyun.com/document_detail/86952.html#UserData */
// request.setUserData("{\"Extend\":{\"test\":\"www\",\"localId\":\"xxxx\"},\"MessageCallback\":{\"CallbackURL\":\"http://test.test.com\"}}");
/* 视频分类ID(可选) */
//request.setCateId(0);
/* 视频标签,多个用逗号分隔(可选) */
//request.setTags("标签1,标签2");
/* 视频描述(可选) */
//request.setDescription("视频描述");
/* 封面图片(可选) */
//request.setCoverURL("http://cover.sample.com/sample.jpg");
/* 模板组ID(可选) */
//request.setTemplateGroupId("8c4792cbc8694e7084fd5330e56a33d");
/* 工作流ID(可选) */
//request.setWorkflowId("d4430d07361f0*be1339577859b0177b");
/* 存储区域(可选) */
//request.setStorageLocation("in-201703232118266-5sejdln9o.oss-cn-shanghai.aliyuncs.com");
/* 开启默认上传进度回调 */
request.setPrintProgress(true);
/* 设置自定义上传进度回调 (必须继承 VoDProgressListener) */
request.setProgressListener(new PutObjectProgressListener());
/* 设置您实现的生成STS信息的接口实现类*/
// request.setVoDRefreshSTSTokenListener(new RefreshSTSTokenImpl());
/* 设置应用ID*/
//request.setAppId("app-1000000");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
/* ECS部署区域*/
request.setEcsRegionId(regionId);
UploadVideoImpl uploader = new UploadVideoImpl();
UploadVideoResponse response = uploader.uploadVideo(request);
return response;
}
/**
* URL网络流上传。支持断点续传,最大支持48.8TB的单个文件。
* 该上传方式需要先将网络文件下载到本地磁盘,再进行上传,所以要保证本地磁盘有充足的空间。
* 当您设置的URL中不包括文件扩展名时,需要单独设置fileExtension,表示文件扩展名。
* @param accessKeyId
* @param accessKeySecret
* @param title
* @param fileName
* @param url
*/
public static UploadURLStreamResponse uploadURLStream(String accessKeyId, String accessKeySecret,String regionId, String title,
String url, String fileExtension,RedisTemplate<String, Object> redisTemplate) {
UploadURLStreamRequest request = new UploadURLStreamRequest(accessKeyId, accessKeySecret, title, url);

/* 文件扩展名*/
request.setFileExtension(fileExtension);
/* 网络文件下载连接超时,单位毫秒,0-表示不限制*/
request.setDownloadConnectTimeout(1000);
/* 网络文件下载读取超时,单位毫秒,0-表示不限制*/
request.setDownloadReadTimeout(0);
/* 网络文件下载后保存的本地目录*/
request.setLocalDownloadFilePath("/aliyunvideo/download");
/* 是否显示水印(可选),指定模板组ID时,根据模板组配置确定是否显示水印*/
//request.setShowWaterMark(true);
/* 自定义消息回调设置(可选),参数说明参考文档 https://help.aliyun.com/document_detail/86952.html#UserData */
// request.setUserData("{\"Extend\":{\"test\":\"www\",\"localId\":\"xxxx\"},\"MessageCallback\":{\"CallbackURL\":\"http://test.test.com\"}}");
/* 视频分类ID(可选) */
//request.setCateId(0);
/* 视频标签,多个用逗号分隔(可选) */
//request.setTags("标签1,标签2");
/* 视频描述(可选) */
//request.setDescription("视频描述");
/* 封面图片(可选) */
//request.setCoverURL("http://cover.sample.com/sample.jpg");
/* 模板组ID(可选) */
//request.setTemplateGroupId("8c4792cbc8694e7084fd5330e56a33d");
/* 工作流ID(可选) */
//request.setWorkflowId("d4430d07361f0*be1339577859b0177b");
/* 存储区域(可选) */
//request.setStorageLocation("in-201703232118266-5sejdln9o.oss-cn-shanghai.aliyuncs.com");
/* 开启默认上传进度回调 */
request.setPrintProgress(true);
/* 设置自定义上传进度回调 (必须继承 VoDProgressListener) */
request.setProgressListener(new PutObjectProgressListener());
/* 设置应用ID*/
//request.setAppId("app-1000000");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
/* ECS部署区域*/
request.setEcsRegionId(regionId);
UploadVideoImpl uploader = new UploadVideoImpl();
UploadURLStreamResponse response = uploader.uploadURLStream(request);
return response;
}

/**
* 文件流上传接口
*
* @param accessKeyId
* @param accessKeySecret
* @param title
* @param fileName
*/
private static void uploadFileStream(String accessKeyId, String accessKeySecret,String regionId, String title, String fileName,
RedisTemplate<String, Object> redisTemplate) {
UploadFileStreamRequest request = new UploadFileStreamRequest(accessKeyId, accessKeySecret, title, fileName);
/* 是否使用默认水印(可选),指定模板组ID时,根据模板组配置确定是否使用默认水印*/
//request.setShowWaterMark(true);
/* 自定义消息回调设置,参数说明参考文档 https://help.aliyun.com/document_detail/86952.html#UserData */
//request.setUserData(""{\"Extend\":{\"test\":\"www\",\"localId\":\"xxxx\"},\"MessageCallback\":{\"CallbackURL\":\"http://test.test.com\"}}"");
/* 视频分类ID(可选) */
//request.setCateId(0);
/* 视频标签,多个用逗号分隔(可选) */
//request.setTags("标签1,标签2");
/* 视频描述(可选) */
//request.setDescription("视频描述");
/* 封面图片(可选) */
//request.setCoverURL("http://cover.sample.com/sample.jpg");
/* 模板组ID(可选) */
//request.setTemplateGroupId("8c4792cbc8694e7084fd5330e56a33d");
/* 工作流ID(可选) */
//request.setWorkflowId("d4430d07361f0*be1339577859b0177b");
/* 存储区域(可选) */
//request.setStorageLocation("in-201703232118266-5sejdln9o.oss-cn-shanghai.aliyuncs.com");
/* 开启默认上传进度回调 */
request.setPrintProgress(true);
/* 设置自定义上传进度回调 (必须继承 VoDProgressListener) */
request.setProgressListener(new PutObjectProgressListener());
/* 设置应用ID*/
//request.setAppId("app-1000000");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
/* ECS部署区域*/
request.setEcsRegionId(regionId);
UploadVideoImpl uploader = new UploadVideoImpl();
UploadFileStreamResponse response = uploader.uploadFileStream(request);
System.out.print("RequestId=" + response.getRequestId() + "\n"); //请求视频点播服务的请求ID
if (response.isSuccess()) {
System.out.print("VideoId=" + response.getVideoId() + "\n");
} else {
/* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */
System.out.print("VideoId=" + response.getVideoId() + "\n");
System.out.print("ErrorCode=" + response.getCode() + "\n");
System.out.print("ErrorMessage=" + response.getMessage() + "\n");
}
}

/**
* 流式上传接口
*
* @param accessKeyId
* @param accessKeySecret
* @param title
* @param fileName
* @param inputStream
*/
public static UploadStreamResponse uploadStream(String accessKeyId, String accessKeySecret,String regionId, String title,
String fileName, InputStream inputStream,RedisTemplate<String, Object> redisTemplate) {
UploadStreamRequest request = new UploadStreamRequest(accessKeyId, accessKeySecret, title, fileName, inputStream);
/* 是否使用默认水印(可选),指定模板组ID时,根据模板组配置确定是否使用默认水印*/
//request.setShowWaterMark(true);
/* 自定义消息回调设置,参数说明参考文档 https://help.aliyun.com/document_detail/86952.html#UserData */
//request.setUserData(""{\"Extend\":{\"test\":\"www\",\"localId\":\"xxxx\"},\"MessageCallback\":{\"CallbackURL\":\"http://test.test.com\"}}"");
/* 视频分类ID(可选) */
//request.setCateId(0);
/* 视频标签,多个用逗号分隔(可选) */
//request.setTags("标签1,标签2");
/* 视频描述(可选) */
//request.setDescription("视频描述");
/* 封面图片(可选) */
//request.setCoverURL("http://cover.sample.com/sample.jpg");
/* 模板组ID(可选) */
//request.setTemplateGroupId("8c4792cbc8694e7084fd5330e56a33d");
/* 工作流ID(可选) */
//request.setWorkflowId("d4430d07361f0*be1339577859b0177b");
/* 存储区域(可选) */
//request.setStorageLocation("in-201703232118266-5sejdln9o.oss-cn-shanghai.aliyuncs.com");
/* 开启默认上传进度回调 */
// request.setPrintProgress(true);
/* 设置自定义上传进度回调 (必须继承 VoDProgressListener) */
// request.setProgressListener(new PutObjectProgressListener());
/* 设置应用ID*/
//request.setAppId("app-1000000");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
/* ECS部署区域*/
// request.setEcsRegionId(regionId);
UploadVideoImpl uploader = new UploadVideoImpl();
UploadStreamResponse response = uploader.uploadStream(request);
return response;
}

/**
* 图片上传接口,本地文件上传示例
* 参数参考文档 https://help.aliyun.com/document_detail/55619.html
*
* @param accessKeyId
* @param accessKeySecret
*/
public static void uploadImageLocalFile(String accessKeyId, String accessKeySecret,String regionId,RedisTemplate<String, Object> redisTemplate) {
/* 图片类型(必选)取值范围:default(默认),cover(封面),watermark(水印)*/
String imageType = "cover";
UploadImageRequest request = new UploadImageRequest(accessKeyId, accessKeySecret, imageType);
request.setImageType("cover");
/* 图片文件扩展名(可选)取值范围:png,jpg,jpeg */
//request.setImageExt("png");
/* 图片标题(可选)长度不超过128个字节,UTF8编码 */
//request.setTitle("图片标题");
/* 图片标签(可选)单个标签不超过32字节,最多不超过16个标签,多个用逗号分隔,UTF8编码 */
//request.setTags("标签1,标签2");
/* 存储区域(可选)*/
//request.setStorageLocation("out-4f3952f78c0211e8b3020013e7.oss-cn-shanghai.aliyuncs.com");
/* 流式上传时,InputStream为必选,fileName为源文件名称,如:文件名称.png(可选)*/
String fileName = "/Users/demo/png/test.png";
request.setFileName(fileName);
/* 开启默认上传进度回调 */
request.setPrintProgress(true);
/* 设置自定义上传进度回调 (必须继承 VoDProgressListener) */
request.setProgressListener(new PutObjectProgressListener());
/* 设置应用ID*/
//request.setAppId("app-1000000");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
UploadImageImpl uploadImage = new UploadImageImpl();
UploadImageResponse response = uploadImage.upload(request);
System.out.print("RequestId=" + response.getRequestId() + "\n");
if (response.isSuccess()) {
System.out.print("ImageId=" + response.getImageId() + "\n");
System.out.print("ImageURL=" + response.getImageURL() + "\n");
} else {
System.out.print("ErrorCode=" + response.getCode() + "\n");
System.out.print("ErrorMessage=" + response.getMessage() + "\n");
}

}

/**
* 图片上传接口,流式上传示例(支持文件流和网络流)
* 参数参考文档 https://help.aliyun.com/document_detail/55619.html
*
* @param accessKeyId
* @param accessKeySecret
*/
public static void uploadImageStream(String accessKeyId, String accessKeySecret,String regionId,RedisTemplate<String, Object> redisTemplate) {
/* 图片类型(必选)取值范围:default(默认),cover(封面),watermark(水印)*/
String imageType = "cover";
UploadImageRequest request = new UploadImageRequest(accessKeyId, accessKeySecret, imageType);
/* 图片文件扩展名(可选)取值范围:png,jpg,jpeg */
//request.setImageExt("png");
/* 图片标题(可选)长度不超过128个字节,UTF8编码 */
//request.setTitle("图片标题");
/* 图片标签(可选)单个标签不超过32字节,最多不超过16个标签,多个用逗号分隔,UTF8编码 */
//request.setTags("标签1,标签2");
/* 存储区域(可选)*/
//request.setStorageLocation("out-4f3952f78c0211e8b3020013e7.oss-cn-shanghai.aliyuncs.com");
/* 流式上传时,InputStream为必选,fileName为源文件名称,如:文件名称.png(可选)*/
//request.setFileName("测试文件名称.png");
/* 开启默认上传进度回调 */
request.setPrintProgress(true);
/* 设置自定义上传进度回调 (必须继承 VoDProgressListener) */
request.setProgressListener(new PutObjectProgressListener());
/* 设置应用ID*/
//request.setAppId("app-1000000");
// 1.文件流上传
// InputStream fileStream = getFileStream(request.getFileName());
// if (fileStream != null) {
// request.setInputStream(fileStream);
// }

// 2.网络流上传
String url = "http://test.aliyun.com/image/default/test.png";
InputStream urlStream = getUrlStream(url);
if (urlStream != null) {
request.setInputStream(urlStream);
}

// 开始上传图片
UploadImageImpl uploadImage = new UploadImageImpl();
UploadImageResponse response = uploadImage.upload(request);
System.out.print("RequestId=" + response.getRequestId() + "\n");
if (response.isSuccess()) {
System.out.print("ImageId=" + response.getImageId() + "\n");
System.out.print("ImageURL=" + response.getImageURL() + "\n");
} else {
System.out.print("ErrorCode=" + response.getCode() + "\n");
System.out.print("ErrorMessage=" + response.getMessage() + "\n");
}
}

/**
* 上传本地m3u8视频或音频文件到点播,m3u8文件和分片文件默认在同一目录(sliceFilenames为空时,会按照同一目录去解析分片地址)
*
* @param request 本地m3u8上传请求类
* m3u8Filename: 本地m3u8索引文件的绝对路径,且m3u8文件的分片信息必须是相对路径,不能含有URL或本地绝对路径
* sliceFilenames: ts分片文件的绝对路径列表,如指定则以此为准,若不指定,则自动解析 m3u8Filename 里的m3u8文件
*/
public static void uploadLocalM3u8(String accessKeyId, String accessKeySecret,String regionId) {
String title = "test_upload_local_m3u8";
String m3u8Filename = "/Users/test/0e9ecfc6da934d1887ed7bdfc925ecf5/cc38da35c7b24de0abe58619cdd7a8-6479a12446b994719838e0307f6458-ld.m3u8";
UploadLocalM3u8Request request = new UploadLocalM3u8Request(accessKeyId, accessKeySecret, title, m3u8Filename);
String[] sliceFilenames = new String[]{
"/Users/test/0e9ecfc6da934d1887ed7bdfc925ecf5/slices/cc38da35c7b24de0abe58619cdd7a896-c45797a1ad6e75fbb9d1a84937034742-ld-00001.ts",
"/Users/test/0e9ecfc6da934d1887ed7bdfc925ecf5/slices/cc38da35c7b24de0abe58619cdd7a896-c45797a1ad6e75fbb9d1a84937034742-ld-00002.ts",
"/Users/test/0e9ecfc6da934d1887ed7bdfc925ecf5/slices/cc38da35c7b24de0abe58619cdd7a896-c45797a1ad6e75fbb9d1a84937034742-ld-00003.ts",
"/Users/test/0e9ecfc6da934d1887ed7bdfc925ecf5/slices/cc38da35c7b24de0abe58619cdd7a896-c45797a1ad6e75fbb9d1a84937034742-ld-00004.ts",
"/Users/test/0e9ecfc6da934d1887ed7bdfc925ecf5/slices/cc38da35c7b24de0abe58619cdd7a896-c45797a1ad6e75fbb9d1a84937034742-ld-00005.ts"
};
// ts分片文件列表,可选,不指定时,直接解析m3u8FileURL获取分片地址
request.setSliceFilenames(sliceFilenames);
/* 可指定分片上传时每个分片的大小,默认为2M字节 */
request.setPartSize(2 * 1024 * 1024L);
/* 可指定分片上传时的并发线程数,默认为1,(注:该配置会占用服务器CPU资源,需根据服务器情况指定)*/
request.setTaskNum(1);
/* 是否显示水印(可选),指定模板组ID时,根据模板组配置确定是否显示水印*/
//request.getShowWaterMark(true);
/* 自定义消息回调设置(可选),参数说明参考文档 https://help.aliyun.com/document_detail/86952.html#UserData */
// request.setUserData("{\"Extend\":{\"test\":\"www\",\"localId\":\"xxxx\"},\"MessageCallback\":{\"CallbackURL\":\"http://test.test.com\"}}");
/* 视频分类ID(可选) */
//request.setCateId(-1L);
/* 视频标签,多个用逗号分隔(可选) */
//request.setTags("标签1,标签2");
/* 视频描述(可选) */
//request.setDescription("视频描述");
/* 封面图片(可选) */
//request.setCoverURL("http://cover.sample.com/sample.jpg");
/* 模板组ID(可选) */
//request.setTemplateGroupId("8c4792cbc8694e7084fd5330e56a33d");
/* 工作流ID(可选) */
//request.setWorkflowId("d4430d07361f0*be1339577859b0177b");
/* 存储区域(可选) */
//request.setStorageLocation("in-201703232118266-5sejdln9o.oss-cn-shanghai.aliyuncs.com");
/* 设置应用ID*/
// request.setAppId("app-1000000");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
/* ECS部署区域*/
request.setEcsRegionId(regionId);
UploadM3u8FileImpl uploadM3u8File = new UploadM3u8FileImpl();
UploadLocalM3u8Response uploadLocalM3u8Response = uploadM3u8File.uploadLocalM3u8(request);
System.out.println("code = " + uploadLocalM3u8Response.getCode());
System.out.println("message = " + uploadLocalM3u8Response.getMessage());
System.out.println("videoId = " + uploadLocalM3u8Response.getVideoId());
System.out.println("requestId = " + uploadLocalM3u8Response.getRequestId());
}

/**
* 上传网络m3u8视频或音频文件到点播,需本地磁盘空间足够,会先下载到本地临时目录,再上传到点播存储
*
* @param request 网络m3u8上传请求类
* m3u8FileURL: 网络m3u8索引文件的URL地址,且m3u8文件的分片信息必须是相对地址,不能含有URL或本地绝对路径
* sliceFileURLs: ts分片文件的URL地址列表;需自行拼接ts分片的URL地址列表
*/
public static void uploadWebM3u8(String accessKeyId, String accessKeySecret,String regionId) {
String title = "test_upload_web_m3u8";
String m3u8FileURL = "http://test.aliyun.com/f0d644abc547129e957b386f772c72/a0e1e2817ab9425aa558fe67a90e717f-538087dcf2c201c31ce4324bf76af691-ld.m3u8";
UploadWebM3u8Request request = new UploadWebM3u8Request(accessKeyId, accessKeySecret, title, m3u8FileURL);
String[] sliceFileURLs = new String[]{
"http://test.aliyun.com/f0d644abc547129e957b386f772c72/a0e1e2817ab9425aa558fe67a90e717f-822598b9c170a8c6dad985e20cd9c27d-ld-00001.ts",
"http://test.aliyun.com/f0d644abc547129e957b386f772c72/a0e1e2817ab9425aa558fe67a90e717f-822598b9c170a8c6dad985e20cd9c27d-ld-00002.ts",
"http://test.aliyun.com/f0d644abc547129e957b386f772c72/a0e1e2817ab9425aa558fe67a90e717f-822598b9c170a8c6dad985e20cd9c27d-ld-00003.ts",
"http://test.aliyun.com/f0d644abc547129e957b386f772c72/a0e1e2817ab9425aa558fe67a90e717f-822598b9c170a8c6dad985e20cd9c27d-ld-00004.ts",
"http://test.aliyun.com/f0d644abc547129e957b386f772c72/a0e1e2817ab9425aa558fe67a90e717f-822598b9c170a8c6dad985e20cd9c27d-ld-00005.ts"
};
// ts分片地址,可选,不指定时,直接解析m3u8FileURL获取分片地址
request.setSliceFileURLs(sliceFileURLs);
/* 下载文件的临时存储目录,可自定义,如不指定则保存到程序所运行的目录下*/
// request.setGlobalLocalFilePath("/User/download/");
/* 可指定分片上传时每个分片的大小,默认为2M字节 */
request.setPartSize(2 * 1024 * 1024L);
/* 可指定分片上传时的并发线程数,默认为1,(注:该配置会占用服务器CPU资源,需根据服务器情况指定)*/
request.setTaskNum(1);
/* 是否显示水印(可选),指定模板组ID时,根据模板组配置确定是否显示水印*/
//request.setShowWaterMark(true);
/* 自定义消息回调设置(可选),参数说明参考文档 https://help.aliyun.com/document_detail/86952.html#UserData */
//request.setUserData("{\"Extend\":{\"test\":\"www\",\"localId\":\"xxxx\"},\"MessageCallback\":{\"CallbackURL\":\"http://test.test.com\"}}");
/* 视频分类ID(可选) */
//request.setCateId(-1L);
/* 视频标签,多个用逗号分隔(可选) */
//request.setTags("标签1,标签2");
/* 视频描述(可选) */
//request.setDescription("视频描述");
/* 封面图片(可选) */
//request.setCoverURL("http://cover.sample.com/sample.jpg");
/* 模板组ID(可选) */
//request.setTemplateGroupId("8c4792cbc8694e7084fd5330e56a33d");
/* 工作流ID(可选) */
//request.setWorkflowId("d4430d07361f0*be1339577859b0177b");
/* 存储区域(可选) */
//request.setStorageLocation("in-201703232118266-5sejdln9o.oss-cn-shanghai.aliyuncs.com");
/* 设置应用ID*/
//request.setAppId("app-1000000");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
/* ECS部署区域*/
request.setEcsRegionId(regionId);
UploadM3u8FileImpl uploadM3u8File = new UploadM3u8FileImpl();
UploadWebM3u8Response uploadWebM3u8Response = uploadM3u8File.uploadWebM3u8(request);
System.out.println("code = " + uploadWebM3u8Response.getCode());
System.out.println("message = " + uploadWebM3u8Response.getMessage());
System.out.println("videoId = " + uploadWebM3u8Response.getVideoId());
System.out.println("requestId = " + uploadWebM3u8Response.getRequestId());
}

/**
* 辅助媒资上传接口,本地文件上传示例
* 参数参考文档 https://help.aliyun.com/document_detail/98467.html
*
* @param accessKeyId
* @param accessKeySecret
*/
public static void uploadAttachedMediaLocalFile(String accessKeyId, String accessKeySecret,String regionId) {
/* 业务类型 */
String businessType = "watermark";
/* 文件扩展名 */
String mediaExt = "png";
String filename = "/Users/demo/png/test.png";
UploadAttachedMediaRequest request = new UploadAttachedMediaRequest(accessKeyId, accessKeySecret, businessType, mediaExt);
request.setFileName(filename);
/* 标题*/
request.setTitle("test_attached_media");
/* 分类ID */
request.setCateId(-1L);
/* 标签,多个用逗号分隔(可选) */
request.setTags("tag1,tag2");
/* 描述(可选) */
request.setDescription("test_desc");
/* 存储区域(可选) */
request.setStorageLocation("out-20170323225118266-5l3hs5gqwa.oss-cn-shanghai.aliyuncs.com");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
/* 设置应用ID*/
//request.setAppId("app-1000000");
UploadAttachedMediaImpl uploader = new UploadAttachedMediaImpl();
UploadAttachedMediaResponse response = uploader.upload(request);
System.out.print("RequestId=" + response.getRequestId() + "\n");
if (response.isSuccess()) {
System.out.print("MediaId=" + response.getMediaId() + "\n");
System.out.print("MediaURL=" + response.getMediaURL() + "\n");
System.out.print("FileURL=" + response.getFileURL() + "\n");
} else {
System.out.print("ErrorCode=" + response.getCode() + "\n");
System.out.print("ErrorMessage=" + response.getMessage() + "\n");
}

}

/**
* 辅助媒资上传接口,流式上传示例(支持文件流和网络流)
* 参数参考文档 https://help.aliyun.com/document_detail/98467.html
*
* @param accessKeyId
* @param accessKeySecret
*/
public static void uploadAttachedMediaStream(String accessKeyId, String accessKeySecret,String regionId) {
/* 业务类型 */
String businessType = "watermark";
/* 文件扩展名 */
String mediaExt = "png";
String filename = "http://test.aliyun.com/test.png";
UploadAttachedMediaRequest request;
// 1.文件流上传
InputStream fileStream = getFileStream(filename);
request = new UploadAttachedMediaRequest(accessKeyId, accessKeySecret, businessType, mediaExt);
request.setInputStream(fileStream);

// 2.网络流上传
// String url = "http://test.aliyun.com/image//test.png";
// InputStream urlStream = getUrlStream(url);
// request = new UploadAttachedMediaRequest(accessKeyId, accessKeySecret, businessType, mediaExt);
// request.setInputStream(urlStream);

/* 标题*/
request.setTitle("test_attached_media");
/* 分类ID */
request.setCateId(-1L);
/* 标签,多个用逗号分隔(可选) */
request.setTags("tag1,tag2");
/* 描述(可选) */
request.setDescription("test_desc");
/* 存储区域(可选) */
request.setStorageLocation("out-20170323225118266-5l3****wa.oss-cn-shanghai.aliyuncs.com");
/* 点播服务接入点 */
request.setApiRegionId(regionId);
/* 设置应用ID*/
// request.setAppId("app-1000000");
// 开始上传
UploadAttachedMediaImpl uploader = new UploadAttachedMediaImpl();
UploadAttachedMediaResponse response = uploader.upload(request);
System.out.print("RequestId=" + response.getRequestId() + "\n");
if (response.isSuccess()) {
System.out.print("MediaId=" + response.getMediaId() + "\n");
System.out.print("MediaURL=" + response.getMediaURL() + "\n");
System.out.print("FileURL=" + response.getFileURL() + "\n");
} else {
System.out.print("ErrorCode=" + response.getCode() + "\n");
System.out.print("ErrorMessage=" + response.getMessage() + "\n");
}
}

private static InputStream getFileStream(String fileName) {
try {
return new FileInputStream(fileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}

private static InputStream getUrlStream(String url) {
try {
return new URL(url).openStream();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String getUploadProgress(RedisTemplate<String, Object> redisTemplate,String videoId) {
return UploadCacheHelper.getUploadProgress(redisTemplate, PutObjectProgressListener.PRE_VIDEO_PROGRESS+videoId);
}
}

+ 0
- 131
mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/upload/PutObjectProgressListener.java Просмотреть файл

@@ -1,131 +0,0 @@
package com.iformall.video.aliyun.sdk.upload;

import org.springframework.data.redis.core.RedisTemplate;

import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.vod.upload.impl.VoDProgressListener;

/**
* 上传进度回调方法类
* 当您开启上传进度回调时该事件回调才会生效。
* OSS分片上传成功或失败均触发相应的回调事件,您可根据业务逻辑处理相应的事件回调。
* 当创建音视频信息成功后,此上传进度回调中的videoId为本次上传生成的视频ID,您可以根据视频ID进行音视频管理。
* 当创建图片信息成功后,此上传进度回调中的ImageId为本次上传生成的图片ID,您可以根据视频ID进行图片管理。
*/

public class PutObjectProgressListener implements VoDProgressListener {
/**
* 已成功上传至OSS的字节数
*/
private long bytesWritten = 0;
/**
* 原始文件的总字节数
*/
private long totalBytes = -1;
/**
* 本次上传成功标记
*/
private boolean succeed = false;
/**
* 视频ID
*/
private String videoId;
/**
* 图片ID
*/
private String imageId;
public static final String PRE_VIDEO_PROGRESS = "video:progress:";
private RedisTemplate<String, Object> redisTemplate;
public void progressChanged(ProgressEvent progressEvent) {
long bytes = progressEvent.getBytes();
ProgressEventType eventType = progressEvent.getEventType();
switch (eventType) {
// 开始上传事件
case TRANSFER_STARTED_EVENT:
if (null != redisTemplate) {
if (videoId != null) {
UploadCacheHelper.cacheStart(redisTemplate, PRE_VIDEO_PROGRESS+videoId);
//System.out.println("qqqqStart to upload videoId " + videoId + "......");
}
if (imageId != null) {
//System.out.println("aaaStart to upload imageId " + imageId + "......");
}
}
break;
// 计算待上传文件总大小事件通知,只有调用本地文件方式上传时支持该事件
case REQUEST_CONTENT_LENGTH_EVENT:
this.totalBytes = bytes;
//System.out.println(this.totalBytes + "bytes in total will be uploaded to OSS.");
break;
// 已经上传成功文件大小事件通知
case REQUEST_BYTE_TRANSFER_EVENT:
this.bytesWritten += bytes;
if (this.totalBytes != -1) {
int percent = (int) (this.bytesWritten * 100.0 / this.totalBytes);
//System.out.println(bytes + " bytes have been written at this time, upload progress: " +
// percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
} else {
//System.out.println(bytes + " bytes have been written at this time, upload sub total : " +
// "(" + this.bytesWritten + ")");
}
UploadCacheHelper.cacheProgress(redisTemplate, PRE_VIDEO_PROGRESS+videoId,String.valueOf(this.bytesWritten));
break;
// 文件全部上传成功事件通知
case TRANSFER_COMPLETED_EVENT:
this.succeed = true;
if (videoId != null) {
UploadCacheHelper.cacheComplete(redisTemplate, PRE_VIDEO_PROGRESS+videoId);
//System.out.println("Succeed to upload videoId " + videoId + " , " + this.bytesWritten + " bytes have been transferred in total.");
}
if (imageId != null) {
//System.out.println("Succeed to upload imageId " + imageId + " , " + this.bytesWritten + " bytes have been transferred in total.");
}
break;
// 文件上传失败事件通知
case TRANSFER_FAILED_EVENT:
if (videoId != null) {
UploadCacheHelper.cacheProgress(redisTemplate, PRE_VIDEO_PROGRESS+videoId,String.valueOf(this.bytesWritten));
System.out.println("Failed to upload videoId " + videoId + " , " + this.bytesWritten + " bytes have been transferred.");
}
if (imageId != null) {
//System.out.println("Failed to upload imageId " + imageId + " , " + this.bytesWritten + " bytes have been transferred.");
}
break;

default:
break;
}
}

public boolean isSucceed() {
return succeed;
}

public void onVidReady(String videoId) {
setVideoId(videoId);
}

public void onImageIdReady(String imageId) {
setImageId(imageId);
}

public String getVideoId() {
return videoId;
}

public void setVideoId(String videoId) {
this.videoId = videoId;
}

public String getImageId() {
return imageId;
}

public void setImageId(String imageId) {
this.imageId = imageId;
}
}


+ 0
- 37
mallinkVideo/src/main/java/com/iformall/video/aliyun/sdk/upload/UploadCacheHelper.java Просмотреть файл

@@ -1,37 +0,0 @@
package com.iformall.video.aliyun.sdk.upload;

import java.util.concurrent.TimeUnit;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

public class UploadCacheHelper {

public static void cacheStart(RedisTemplate<String, Object> template,String key) {
ValueOperations<String, Object> operations = template.opsForValue();
operations.set(key, "begin", 24*3600,TimeUnit.SECONDS);
}
public static void cacheComplete(RedisTemplate<String, Object> template,String key) {
ValueOperations<String, Object> operations = template.opsForValue();
operations.set(key, "complete", 24*3600,TimeUnit.SECONDS);
}
public static void cacheProgress(RedisTemplate<String, Object> template,String key,String bytes) {
ValueOperations<String, Object> operations = template.opsForValue();
operations.set(key, bytes, 24*3600,TimeUnit.SECONDS);
}
public static String getUploadProgress(RedisTemplate<String, Object> template,String key) {
if (template.hasKey(key)) {
ValueOperations<String, Object> operations = template.opsForValue();
Object o = operations.get(key);
if (o instanceof String) {
return (String)o;
}
}
return null;
}
}

+ 50
- 0
mallinkVideo/src/main/java/com/iformall/video/aliyun/thread/AliyunVedioThreadExecutorConfig.java Просмотреть файл

@@ -0,0 +1,50 @@
package com.iformall.video.aliyun.thread;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.iformall.video.aliyun.config.AliyunVideoConfig;

import lombok.Data;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@Configuration
@EnableAsync
@ConfigurationProperties(prefix = "video.aliyun")
public class AliyunVedioThreadExecutorConfig {

private int corePoolSize;
private int maxPoolSize;
private int queueCapacity;
private String namePrefix;
@Bean(name = "asyncVideoServiceExecutor")
public Executor asyncVideoServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(namePrefix);

// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}

}

+ 32
- 0
mallinkVideo/src/main/java/com/iformall/video/aliyun/thread/AliyunVideoUploadThread.java Просмотреть файл

@@ -0,0 +1,32 @@
package com.iformall.video.aliyun.thread;

import org.springframework.stereotype.Service;

import com.aliyuncs.vod.model.v20170321.CreateUploadVideoResponse;
import com.iformall.video.aliyun.sdk.server.AliyunVedioServer;
import com.iformall.video.aliyun.sdk.server.UploadCacheHelper;

import lombok.extern.slf4j.Slf4j;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;


//https://blog.csdn.net/m0_37701381/article/details/81072774
@Service
@Slf4j
public class AliyunVideoUploadThread {

@Async("asyncVideoServiceExecutor")
public void executeAysncTask(CreateUploadVideoResponse response,String localFile,RedisTemplate<String, Object> redisTemplate) throws InterruptedException {
try {
AliyunVedioServer.uploadVideo(response, localFile,redisTemplate);
} catch (UnsupportedEncodingException e) {
log.error("AliyunVideoUploadThread upload error."+response.getVideoId(),e);
UploadCacheHelper.cacheError(redisTemplate, response.getVideoId());
}
}
}

+ 0
- 5
pom.xml Просмотреть файл

@@ -156,11 +156,6 @@
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>


Загрузка…
Отмена
Сохранить