fix: 新需求
This commit is contained in:
parent
06fb0de039
commit
3a05e41f79
|
|
@ -10,10 +10,10 @@ import com.ruoyi.common.annotation.Anonymous;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.core.domain.model.LoginAiUser;
|
import com.ruoyi.common.core.domain.model.LoginAiUser;
|
||||||
import com.ruoyi.common.utils.AwsS3Util;
|
|
||||||
import com.ruoyi.common.utils.RandomStringUtil;
|
import com.ruoyi.common.utils.RandomStringUtil;
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.TencentCosUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
@ -36,13 +36,21 @@ import java.util.regex.Pattern;
|
||||||
public class ByteApiController extends BaseController {
|
public class ByteApiController extends BaseController {
|
||||||
|
|
||||||
private final IByteService byteService;
|
private final IByteService byteService;
|
||||||
private final AwsS3Util awsS3Util;
|
private final TencentCosUtil tencentCosUtil;
|
||||||
private final IAiOrderService aiOrderService;
|
private final IAiOrderService aiOrderService;
|
||||||
private final IAiManagerService managerService;
|
private final IAiManagerService managerService;
|
||||||
private final IAiTagService aiTagService;
|
private final IAiTagService aiTagService;
|
||||||
@Value("${byteapi.callBackUrl}")
|
@Value("${byteapi.callBackUrl}")
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
|
// 火山引擎配置
|
||||||
|
@Value("${volcengine.ark.apiKey}")
|
||||||
|
private String volcApiKey;
|
||||||
|
@Value("${volcengine.ark.baseUrl}")
|
||||||
|
private String volcBaseUrl;
|
||||||
|
@Value("${volcengine.ark.callbackUrl}")
|
||||||
|
private String volcCallbackUrl;
|
||||||
|
|
||||||
@PostMapping("/promptToImg")
|
@PostMapping("/promptToImg")
|
||||||
@ApiOperation("文生图")
|
@ApiOperation("文生图")
|
||||||
public AjaxResult promptToImg(@RequestBody ByteApiRequest request) {
|
public AjaxResult promptToImg(@RequestBody ByteApiRequest request) {
|
||||||
|
|
@ -85,7 +93,9 @@ public class ByteApiController extends BaseController {
|
||||||
}
|
}
|
||||||
aiOrder.setText(text);
|
aiOrder.setText(text);
|
||||||
ByteBodyReq byteBodyReq = new ByteBodyReq();
|
ByteBodyReq byteBodyReq = new ByteBodyReq();
|
||||||
byteBodyReq.setModel("ep-20251104104536-2gpgz");
|
// model由前端传入,默认为Seedance 2.0
|
||||||
|
byteBodyReq.setModel(StringUtils.isNotEmpty(request.getModel()) ?
|
||||||
|
request.getModel() : "ep-20260326165811-dlkth");
|
||||||
byteBodyReq.setPrompt(text);
|
byteBodyReq.setPrompt(text);
|
||||||
byteBodyReq.setSequential_image_generation("disabled");
|
byteBodyReq.setSequential_image_generation("disabled");
|
||||||
byteBodyReq.setResponse_format("url");
|
byteBodyReq.setResponse_format("url");
|
||||||
|
|
@ -96,7 +106,7 @@ public class ByteApiController extends BaseController {
|
||||||
List<ByteDataRes> data = byteBodyRes.getData();
|
List<ByteDataRes> data = byteBodyRes.getData();
|
||||||
ByteDataRes byteDataRes = data.get(0);
|
ByteDataRes byteDataRes = data.get(0);
|
||||||
String url = byteDataRes.getUrl();
|
String url = byteDataRes.getUrl();
|
||||||
url = awsS3Util.uploadFileByUrl(url);
|
url = tencentCosUtil.uploadFileByUrl(url);
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
// 判断生成失败,退回金额逻辑
|
// 判断生成失败,退回金额逻辑
|
||||||
aiOrderService.orderFailure(aiOrder);
|
aiOrderService.orderFailure(aiOrder);
|
||||||
|
|
@ -175,7 +185,7 @@ public class ByteApiController extends BaseController {
|
||||||
List<ByteDataRes> data = byteBodyRes.getData();
|
List<ByteDataRes> data = byteBodyRes.getData();
|
||||||
ByteDataRes byteDataRes = data.get(0);
|
ByteDataRes byteDataRes = data.get(0);
|
||||||
String url = byteDataRes.getUrl();
|
String url = byteDataRes.getUrl();
|
||||||
url = awsS3Util.uploadFileByUrl(url);
|
url = tencentCosUtil.uploadFileByUrl(url);
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
// 判断生成失败,退回金额逻辑
|
// 判断生成失败,退回金额逻辑
|
||||||
aiOrderService.orderFailure(aiOrder);
|
aiOrderService.orderFailure(aiOrder);
|
||||||
|
|
@ -191,7 +201,7 @@ public class ByteApiController extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/imgToVideo")
|
@PostMapping("/imgToVideo")
|
||||||
@ApiOperation("图生视频")
|
@ApiOperation("图生视频 (Seedance 2.0)")
|
||||||
public AjaxResult imgToVideo(@RequestBody ByteApiRequest request) throws Exception {
|
public AjaxResult imgToVideo(@RequestBody ByteApiRequest request) throws Exception {
|
||||||
String functionType = request.getFunctionType();
|
String functionType = request.getFunctionType();
|
||||||
if (null == functionType) {
|
if (null == functionType) {
|
||||||
|
|
@ -235,51 +245,56 @@ public class ByteApiController extends BaseController {
|
||||||
return AjaxResult.error(-1, "You have a low balance, please recharge");
|
return AjaxResult.error(-1, "You have a low balance, please recharge");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// String text = request.getText();
|
|
||||||
// if (StringUtils.isBlank(text)) {
|
|
||||||
// return AjaxResult.error("text is null");
|
|
||||||
// }
|
|
||||||
// String tags = request.getTags();
|
|
||||||
// if (StringUtils.isNotBlank(tags)) {
|
|
||||||
// text = "(优先考虑以下关键词:" + tags + ")";
|
|
||||||
// }
|
|
||||||
aiOrder.setText(text);
|
aiOrder.setText(text);
|
||||||
|
|
||||||
aiOrder.setImg1(firstUrl.toString());
|
aiOrder.setImg1(firstUrl.toString());
|
||||||
Integer duration = request.getDuration();
|
|
||||||
|
Integer duration = request.getDuration() != null ? request.getDuration() : 4;
|
||||||
|
|
||||||
ByteBodyReq byteBodyReq = new ByteBodyReq();
|
ByteBodyReq byteBodyReq = new ByteBodyReq();
|
||||||
byteBodyReq.setModel("ep-20251113072240-cfxlz");
|
// model由前端传入,默认为Seedance2.0
|
||||||
byteBodyReq.setCallback_url(url + "/api/ai/callBack");
|
byteBodyReq.setModel(StringUtils.isNotEmpty(request.getModel()) ?
|
||||||
List<ContentItem> content = new ArrayList<>();
|
request.getModel() : "ep-20260326165811-dlkth");
|
||||||
ContentItem contentItem = new ContentItem();
|
byteBodyReq.setCallback_url(volcCallbackUrl);
|
||||||
contentItem.setType("text");
|
|
||||||
contentItem.setText(text + " --dur " + duration + " --fps 24 --rs 720p --wm false --cf false");
|
|
||||||
content.add(contentItem);
|
|
||||||
|
|
||||||
ContentItem contentItem1 = new ContentItem();
|
// 构建符合火山引擎格式的content
|
||||||
contentItem1.setType("image_url");
|
List<ContentItem> contentList = new ArrayList<>();
|
||||||
contentItem1.setRole("first_frame");
|
|
||||||
ImageUrl imageUrl1 = new ImageUrl();
|
|
||||||
imageUrl1.setUrl(firstUrl.toString());
|
|
||||||
contentItem1.setImageUrl(imageUrl1);
|
|
||||||
content.add(contentItem1);
|
|
||||||
|
|
||||||
|
// 文本提示词
|
||||||
|
ContentItem textItem = new ContentItem();
|
||||||
|
textItem.setType("text");
|
||||||
|
textItem.setText(text);
|
||||||
|
contentList.add(textItem);
|
||||||
|
|
||||||
|
// 首帧图片
|
||||||
|
ContentItem firstFrameItem = new ContentItem();
|
||||||
|
firstFrameItem.setType("image_url");
|
||||||
|
firstFrameItem.setRole("first_frame");
|
||||||
|
ImageUrl firstImageUrl = new ImageUrl();
|
||||||
|
firstImageUrl.setUrl(firstUrl.toString());
|
||||||
|
firstFrameItem.setImageUrl(firstImageUrl);
|
||||||
|
contentList.add(firstFrameItem);
|
||||||
|
|
||||||
|
// 如果有尾帧
|
||||||
String lastUrl = request.getLastUrl();
|
String lastUrl = request.getLastUrl();
|
||||||
if (StringUtils.isNotBlank(lastUrl)) {
|
if (StringUtils.isNotBlank(lastUrl)) {
|
||||||
ContentItem contentItem2 = new ContentItem();
|
ContentItem lastFrameItem = new ContentItem();
|
||||||
contentItem2.setType("image_url");
|
lastFrameItem.setType("image_url");
|
||||||
contentItem2.setRole("last_frame");
|
lastFrameItem.setRole("last_frame");
|
||||||
ImageUrl imageUrl2 = new ImageUrl();
|
ImageUrl lastImageUrl = new ImageUrl();
|
||||||
imageUrl2.setUrl(lastUrl);
|
lastImageUrl.setUrl(lastUrl);
|
||||||
contentItem2.setImageUrl(imageUrl2);
|
lastFrameItem.setImageUrl(lastImageUrl);
|
||||||
content.add(contentItem2);
|
contentList.add(lastFrameItem);
|
||||||
aiOrder.setImg2(lastUrl);
|
aiOrder.setImg2(lastUrl);
|
||||||
}
|
}
|
||||||
byteBodyReq.setContent(content);
|
|
||||||
|
byteBodyReq.setContent(contentList);
|
||||||
|
byteBodyReq.setDuration(duration);
|
||||||
|
byteBodyReq.setResolution("720p");
|
||||||
|
byteBodyReq.setRatio("3:4");
|
||||||
|
|
||||||
ByteBodyRes byteBodyRes = byteService.imgToVideo(byteBodyReq);
|
ByteBodyRes byteBodyRes = byteService.imgToVideo(byteBodyReq);
|
||||||
String id = byteBodyRes.getId();
|
String id = byteBodyRes.getId();
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
// 判断生成失败,退回金额逻辑
|
|
||||||
aiOrderService.orderFailure(aiOrder);
|
aiOrderService.orderFailure(aiOrder);
|
||||||
return AjaxResult.error(-2, "generation failed, balance has been refunded");
|
return AjaxResult.error(-2, "generation failed, balance has been refunded");
|
||||||
}
|
}
|
||||||
|
|
@ -299,7 +314,7 @@ public class ByteApiController extends BaseController {
|
||||||
if ("succeeded".equals(byteBodyRes.getStatus())) {
|
if ("succeeded".equals(byteBodyRes.getStatus())) {
|
||||||
content content = byteBodyRes.getContent();
|
content content = byteBodyRes.getContent();
|
||||||
String videoUrl = content.getVideo_url();
|
String videoUrl = content.getVideo_url();
|
||||||
videoUrl = awsS3Util.uploadFileByUrl(videoUrl);
|
videoUrl = tencentCosUtil.uploadFileByUrl(videoUrl);
|
||||||
content.setVideo_url(videoUrl);
|
content.setVideo_url(videoUrl);
|
||||||
AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(id);
|
AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(id);
|
||||||
AiOrder aiOrder = new AiOrder();
|
AiOrder aiOrder = new AiOrder();
|
||||||
|
|
@ -311,24 +326,28 @@ public class ByteApiController extends BaseController {
|
||||||
return AjaxResult.success(byteBodyRes);
|
return AjaxResult.success(byteBodyRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/callBack")
|
@GetMapping(value = "/volcCallback")
|
||||||
@ApiOperation("视频下载回调")
|
@ApiOperation("火山引擎视频回调")
|
||||||
@Anonymous
|
@Anonymous
|
||||||
public AjaxResult callBack(@PathVariable("id") ByteBodyRes byteBodyRes) throws Exception {
|
public AjaxResult volcCallback(@RequestBody ByteBodyRes byteBodyRes) throws Exception {
|
||||||
if ("succeeded".equals(byteBodyRes.getStatus())) {
|
if ("succeeded".equals(byteBodyRes.getStatus())) {
|
||||||
String id = byteBodyRes.getId();
|
String id = byteBodyRes.getId();
|
||||||
content content = byteBodyRes.getContent();
|
content contentObj = byteBodyRes.getContent();
|
||||||
String videoUrl = content.getVideo_url();
|
if (contentObj != null && StringUtils.isNotEmpty(contentObj.getVideo_url())) {
|
||||||
videoUrl = awsS3Util.uploadFileByUrl(videoUrl);
|
String videoUrl = contentObj.getVideo_url();
|
||||||
content.setVideo_url(videoUrl);
|
videoUrl = tencentCosUtil.uploadFileByUrl(videoUrl);
|
||||||
AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(id);
|
contentObj.setVideo_url(videoUrl);
|
||||||
AiOrder aiOrder = new AiOrder();
|
|
||||||
aiOrder.setId(aiOrderByResult.getId());
|
AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(id);
|
||||||
aiOrder.setResult(videoUrl);
|
if (aiOrderByResult != null) {
|
||||||
// aiOrder.setUpdateBy(SecurityUtils.getLoginAiUser().getUsername());
|
AiOrder aiOrder = new AiOrder();
|
||||||
aiOrderService.updateAiOrder(aiOrder);
|
aiOrder.setId(aiOrderByResult.getId());
|
||||||
|
aiOrder.setResult(videoUrl);
|
||||||
|
aiOrderService.updateAiOrder(aiOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return AjaxResult.success(byteBodyRes);
|
return AjaxResult.success("callback success");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.ruoyi.api;
|
package com.ruoyi.api;
|
||||||
|
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.utils.AwsS3Util;
|
import com.ruoyi.common.utils.TencentCosUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import io.swagger.annotations.ApiParam;
|
import io.swagger.annotations.ApiParam;
|
||||||
|
|
@ -18,7 +18,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
@Api(tags = "文件上传")
|
@Api(tags = "文件上传")
|
||||||
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
||||||
public class FileController {
|
public class FileController {
|
||||||
private final AwsS3Util awsS3Util;
|
private final TencentCosUtil tencentCosUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件上传
|
* 文件上传
|
||||||
|
|
@ -30,7 +30,7 @@ public class FileController {
|
||||||
@ApiParam(name = "file", value = "文件", required = true)
|
@ApiParam(name = "file", value = "文件", required = true)
|
||||||
@RequestParam("file") MultipartFile file) throws Exception {
|
@RequestParam("file") MultipartFile file) throws Exception {
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
String uploadUrl = awsS3Util.uploadMultipartFile(file, true);
|
String uploadUrl = tencentCosUtil.uploadMultipartFile(file, true);
|
||||||
ajax.put("url", uploadUrl);
|
ajax.put("url", uploadUrl);
|
||||||
return ajax;
|
return ajax;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ public class ByteApiRequest {
|
||||||
@ApiModelProperty(name = "标签字符串")
|
@ApiModelProperty(name = "标签字符串")
|
||||||
private String tags;
|
private String tags;
|
||||||
|
|
||||||
|
@ApiModelProperty(name = "使用的模型")
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import com.ruoyi.common.annotation.Anonymous;
|
||||||
import com.ruoyi.common.config.RuoYiConfig;
|
import com.ruoyi.common.config.RuoYiConfig;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.exception.base.BaseException;
|
import com.ruoyi.common.exception.base.BaseException;
|
||||||
import com.ruoyi.common.utils.AwsS3Util;
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.common.utils.TencentCosUtil;
|
import com.ruoyi.common.utils.TencentCosUtil;
|
||||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||||
|
|
@ -36,7 +35,6 @@ public class CommonController {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
|
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
|
||||||
private final ServerConfig serverConfig;
|
private final ServerConfig serverConfig;
|
||||||
private final TencentCosUtil tencentCosUtil;
|
private final TencentCosUtil tencentCosUtil;
|
||||||
private final AwsS3Util awsS3Util;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用下载请求
|
* 通用下载请求
|
||||||
|
|
@ -158,7 +156,7 @@ public class CommonController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AWS上传请求(单个)
|
* 腾讯云COS上传请求(单个)
|
||||||
*/
|
*/
|
||||||
@ApiOperation("图片上传接口")
|
@ApiOperation("图片上传接口")
|
||||||
@PostMapping("/aws/upload")
|
@PostMapping("/aws/upload")
|
||||||
|
|
@ -166,7 +164,7 @@ public class CommonController {
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
String uploadUrl;
|
String uploadUrl;
|
||||||
try {
|
try {
|
||||||
uploadUrl = awsS3Util.uploadMultipartFile(file, true);
|
uploadUrl = tencentCosUtil.uploadMultipartFile(file, true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return AjaxResult.error(e.getMessage());
|
return AjaxResult.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,24 +209,32 @@ google:
|
||||||
redirect-uri:
|
redirect-uri:
|
||||||
|
|
||||||
tencentCos:
|
tencentCos:
|
||||||
accessKey:
|
accessKey: ${TENCENT_COS_SECRET_ID:}
|
||||||
secretKey:
|
secretKey: ${TENCENT_COS_SECRET_KEY:}
|
||||||
endpoint:
|
endpoint: ap-guangzhou
|
||||||
bucketName:
|
bucketName: seedance-1331490964
|
||||||
domain:
|
domain: https://seedance-1331490964.cos.ap-guangzhou.myqcloud.com
|
||||||
|
|
||||||
aws:
|
# aws配置已替换为腾讯云COS,请在环境变量或配置文件中设置腾讯云凭证
|
||||||
accessKey: AKIAYVMHEVDDZQGE3HVX
|
# aws:
|
||||||
secretKey: B9nxdferMhdRuxzoKeQam/NxiVvIhI7lSru6VfwG
|
# accessKey: AKIAYVMHEVDDZQGE3HVX
|
||||||
endpoint: ap-southeast-1
|
# secretKey: B9nxdferMhdRuxzoKeQam/NxiVvIhI7lSru6VfwG
|
||||||
bucketName: di-image
|
# endpoint: ap-southeast-1
|
||||||
domain: https://images.iqyjsnwv.com/
|
# bucketName: di-image
|
||||||
|
# domain: https://images.iqyjsnwv.com/
|
||||||
|
|
||||||
byteapi:
|
byteapi:
|
||||||
url: https://ark.ap-southeast.bytepluses.com/api/v3
|
url: https://ark.ap-southeast.bytepluses.com/api/v3
|
||||||
apiKey: 327d2815-2516-44c2-9e32-2dc50bf7afd7
|
apiKey: 327d2815-2516-44c2-9e32-2dc50bf7afd7
|
||||||
callBackUrl: https://undressing.top
|
callBackUrl: https://undressing.top
|
||||||
|
|
||||||
|
# 火山引擎 Ark API (Seedance 2.0)
|
||||||
|
volcengine:
|
||||||
|
ark:
|
||||||
|
baseUrl: https://ark.cn-beijing.volces.com
|
||||||
|
apiKey: ${VOLCENGINE_ARK_API_KEY:sk-XXXXXXXXXXXXXXXX}
|
||||||
|
callbackUrl: https://undressing.top/api/ai/volcCallback
|
||||||
|
|
||||||
jinsha:
|
jinsha:
|
||||||
url: https://api.jinshapay.xyz
|
url: https://api.jinshapay.xyz
|
||||||
appId: 1763617360
|
appId: 1763617360
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,13 @@ import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
|
@ -33,22 +39,32 @@ public class TencentCosUtil {
|
||||||
private String domain;
|
private String domain;
|
||||||
|
|
||||||
|
|
||||||
//文件上传
|
/**
|
||||||
|
* 上传MultipartFile到腾讯云COS,返回文件访问地址
|
||||||
|
* 与AwsS3Util.uploadMultipartFile方法接口兼容
|
||||||
|
*/
|
||||||
public String upload(MultipartFile file) {
|
public String upload(MultipartFile file) {
|
||||||
|
return uploadMultipartFile(file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传MultipartFile到腾讯云COS,返回文件访问地址
|
||||||
|
*
|
||||||
|
* @param file 前端上传的MultipartFile
|
||||||
|
* @param isPublic 是否公开访问(当前实现中忽略,使用domain配置)
|
||||||
|
* @return 文件访问地址(URL字符串)
|
||||||
|
*/
|
||||||
|
public String uploadMultipartFile(MultipartFile file, boolean isPublic) throws Exception {
|
||||||
|
if (file.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("上传文件不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
// 3 生成 cos 客户端。
|
|
||||||
COSClient cosClient = createCosClient();
|
COSClient cosClient = createCosClient();
|
||||||
|
|
||||||
// 存储桶的命名格式为 BucketName-APPID,此处填写的存储桶名称必须为此格式
|
// 生成唯一文件键,格式与AWS一致:yyyy/MM/dd/uuid_filename
|
||||||
// 对象键(Key)是对象在存储桶中的唯一标识。 998u-09iu-09i-333
|
String key = generateCosKey(file.getOriginalFilename());
|
||||||
//在文件名称前面添加uuid值
|
|
||||||
String key = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8) + "_"
|
|
||||||
+ file.getOriginalFilename();
|
|
||||||
//对上传文件分组,根据当前日期 /2022/11/11
|
|
||||||
String dateTime = new DateTime().toString("yyyy/MM/dd");
|
|
||||||
key = dateTime + "/" + key;
|
|
||||||
try {
|
try {
|
||||||
//获取上传文件输入流
|
|
||||||
InputStream inputStream = file.getInputStream();
|
InputStream inputStream = file.getInputStream();
|
||||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||||
PutObjectRequest putObjectRequest = new PutObjectRequest(
|
PutObjectRequest putObjectRequest = new PutObjectRequest(
|
||||||
|
|
@ -56,17 +72,41 @@ public class TencentCosUtil {
|
||||||
key,
|
key,
|
||||||
inputStream,
|
inputStream,
|
||||||
objectMetadata);
|
objectMetadata);
|
||||||
// 高级接口会返回一个异步结果Upload
|
|
||||||
PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
|
PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
|
||||||
|
|
||||||
//返回上传文件路径
|
// 返回COS文件访问地址
|
||||||
//https://ggkt-atguigu-1310644373.cos.ap-beijing.myqcloud.com/01.jpg
|
String url = domain + (domain.endsWith("/") ? "" : "/") + key;
|
||||||
String url = domain + "/" + key;
|
|
||||||
return url;
|
return url;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException("上传文件到COS失败: " + e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
cosClient.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过URL下载文件并上传到COS
|
||||||
|
* 与AwsS3Util.uploadFileByUrl方法接口兼容
|
||||||
|
*/
|
||||||
|
public String uploadFileByUrl(String fileUrl) throws Exception {
|
||||||
|
return uploadFileByUrl(fileUrl, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String uploadFileByUrl(String fileUrl, boolean isPublic) throws Exception {
|
||||||
|
if (fileUrl == null || fileUrl.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("文件下载链接不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
Path tempPath = downloadFileToTemp(fileUrl);
|
||||||
|
try {
|
||||||
|
// 使用临时文件上传
|
||||||
|
MultipartFile multipartFile = createMultipartFileFromPath(tempPath, extractFileNameFromUrl(fileUrl));
|
||||||
|
return uploadMultipartFile(multipartFile, isPublic);
|
||||||
|
} finally {
|
||||||
|
Files.deleteIfExists(tempPath);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,7 +118,107 @@ public class TencentCosUtil {
|
||||||
ClientConfig clientConfig = new ClientConfig(region);
|
ClientConfig clientConfig = new ClientConfig(region);
|
||||||
// 这里建议设置使用 https 协议
|
// 这里建议设置使用 https 协议
|
||||||
clientConfig.setHttpProtocol(HttpProtocol.https);
|
clientConfig.setHttpProtocol(HttpProtocol.https);
|
||||||
|
clientConfig.setConnectionTimeout(30 * 1000); // 连接超时30秒
|
||||||
|
clientConfig.setSocketTimeout(60 * 1000); // 读取超时60秒
|
||||||
//1.3 生成cos客户端
|
//1.3 生成cos客户端
|
||||||
return new COSClient(credentials, clientConfig);
|
return new COSClient(credentials, clientConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成COS文件键,与AWS保持一致的命名格式
|
||||||
|
*/
|
||||||
|
private String generateCosKey(String originalFileName) {
|
||||||
|
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
|
||||||
|
String dateTime = new DateTime().toString("yyyy/MM/dd");
|
||||||
|
return dateTime + "/" + uuid + "_" + originalFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件到临时路径
|
||||||
|
*/
|
||||||
|
private Path downloadFileToTemp(String fileUrl) throws Exception {
|
||||||
|
String suffix = getFileSuffixFromUrl(fileUrl);
|
||||||
|
Path tempPath = Files.createTempFile("url-upload-", suffix);
|
||||||
|
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
InputStream in = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL url = new URL(fileUrl);
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
connection.setConnectTimeout(5000);
|
||||||
|
connection.setReadTimeout(10000);
|
||||||
|
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode < 200 || responseCode >= 300) {
|
||||||
|
throw new RuntimeException("文件下载失败,状态码:" + responseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
in = connection.getInputStream();
|
||||||
|
out = Files.newOutputStream(tempPath);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
try { out.close(); } catch (IOException e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
if (in != null) {
|
||||||
|
try { in.close(); } catch (IOException e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
if (connection != null) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractFileNameFromUrl(String fileUrl) {
|
||||||
|
String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
|
||||||
|
if (fileName.contains("?")) {
|
||||||
|
fileName = fileName.split("\\?")[0];
|
||||||
|
}
|
||||||
|
return fileName.isEmpty() ? "default_file" : fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFileSuffixFromUrl(String fileUrl) {
|
||||||
|
String fileName = extractFileNameFromUrl(fileUrl);
|
||||||
|
if (fileName.contains(".")) {
|
||||||
|
return fileName.substring(fileName.lastIndexOf("."));
|
||||||
|
}
|
||||||
|
return ".tmp";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Path转换为MultipartFile(简单实现,用于uploadFileByUrl)
|
||||||
|
*/
|
||||||
|
private MultipartFile createMultipartFileFromPath(Path path, String originalFilename) throws IOException {
|
||||||
|
byte[] bytes = Files.readAllBytes(path);
|
||||||
|
return new MultipartFile() {
|
||||||
|
@Override
|
||||||
|
public String getName() { return "file"; }
|
||||||
|
@Override
|
||||||
|
public String getOriginalFilename() { return originalFilename; }
|
||||||
|
@Override
|
||||||
|
public String getContentType() { return null; }
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() { return bytes.length == 0; }
|
||||||
|
@Override
|
||||||
|
public long getSize() { return bytes.length; }
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() throws IOException { return bytes; }
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException { return Files.newInputStream(path); }
|
||||||
|
@Override
|
||||||
|
public void transferTo(java.io.File dest) throws IOException, IllegalStateException {
|
||||||
|
Files.copy(path, dest.toPath());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,10 @@ public class ByteBodyReq {
|
||||||
@JsonProperty("content")
|
@JsonProperty("content")
|
||||||
private List<ContentItem> content;
|
private List<ContentItem> content;
|
||||||
|
|
||||||
|
private Integer duration;
|
||||||
|
private String resolution;
|
||||||
|
private String ratio;
|
||||||
|
private Integer seed;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,13 @@ public class ByteService implements IByteService {
|
||||||
@Value("${byteapi.apiKey}")
|
@Value("${byteapi.apiKey}")
|
||||||
private String apiKey;
|
private String apiKey;
|
||||||
|
|
||||||
|
// 火山引擎配置
|
||||||
|
@Value("${volcengine.ark.baseUrl:https://ark.cn-beijing.volces.com}")
|
||||||
|
private String volcBaseUrl;
|
||||||
|
|
||||||
|
@Value("${volcengine.ark.apiKey}")
|
||||||
|
private String volcApiKey;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBodyRes promptToImg(ByteBodyReq req) throws Exception {
|
public ByteBodyRes promptToImg(ByteBodyReq req) throws Exception {
|
||||||
return this.imgToImg(req);
|
return this.imgToImg(req);
|
||||||
|
|
@ -75,70 +82,63 @@ public class ByteService implements IByteService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBodyRes imgToVideo(ByteBodyReq req) throws Exception {
|
public ByteBodyRes imgToVideo(ByteBodyReq req) throws Exception {
|
||||||
|
if (req == null) {
|
||||||
|
throw new Exception("imgToVideo error:req is null");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 验证请求参数(可选,根据业务需求)
|
// 使用火山引擎配置
|
||||||
// if (StringUtils.isBlank(req.getPrompt())) {
|
|
||||||
// throw new Exception("imgToVideo error:prompt is null");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 2. 构建请求体JSON(基于ByteBodyReq的字段)
|
|
||||||
// 注意:ByteBodyReq需包含与API参数对应的字段(model、prompt等)
|
|
||||||
String jsonBody = objectMapper.writeValueAsString(req);
|
String jsonBody = objectMapper.writeValueAsString(req);
|
||||||
// 3. 构建请求
|
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(API_URL + "/contents/generations/tasks")
|
.url(volcBaseUrl + "/api/v3/contents/generations/tasks")
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.header("Authorization", "Bearer " + apiKey)
|
.header("Authorization", "Bearer " + volcApiKey)
|
||||||
.post(RequestBody.create(
|
.post(RequestBody.create(
|
||||||
MediaType.parse("application/json; charset=utf-8"),
|
MediaType.parse("application/json; charset=utf-8"),
|
||||||
jsonBody
|
jsonBody
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
// 4. 发送同步请求(因方法需要返回值,使用execute而非enqueue)
|
|
||||||
Response response = OkHttpUtils.newCall(request).execute();
|
Response response = OkHttpUtils.newCall(request).execute();
|
||||||
// 5. 处理响应
|
|
||||||
if (!response.isSuccessful()) {
|
if (!response.isSuccessful()) {
|
||||||
// 非200状态:返回错误信息(假设ByteBodyRes有error字段)
|
|
||||||
String errorMsg = response.body() != null ? response.body().string() : "imgToVideo error";
|
String errorMsg = response.body() != null ? response.body().string() : "imgToVideo error";
|
||||||
throw new Exception("imgToVideo error:" + errorMsg);
|
throw new Exception("imgToVideo error:" + errorMsg);
|
||||||
}
|
}
|
||||||
// 6. 解析成功响应为ByteBodyRes
|
|
||||||
if (response.body() == null) {
|
if (response.body() == null) {
|
||||||
throw new Exception("imgToVideo response null");
|
throw new Exception("imgToVideo response null");
|
||||||
}
|
}
|
||||||
|
|
||||||
String responseBody = response.body().string();
|
String responseBody = response.body().string();
|
||||||
return objectMapper.readValue(responseBody, ByteBodyRes.class);
|
return objectMapper.readValue(responseBody, ByteBodyRes.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBodyRes uploadVideo(String id) throws Exception {
|
public ByteBodyRes uploadVideo(String id) throws Exception {
|
||||||
// 1. 验证请求参数(可选,根据业务需求)
|
|
||||||
if (StringUtils.isBlank(id)) {
|
if (StringUtils.isBlank(id)) {
|
||||||
throw new Exception("uploadVideo error:id is null");
|
throw new Exception("uploadVideo error:id is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 构建请求体JSON(基于ByteBodyReq的字段)
|
// 使用火山引擎配置查询任务状态
|
||||||
// 注意:ByteBodyReq需包含与API参数对应的字段(model、prompt等)
|
|
||||||
//String jsonBody = objectMapper.writeValueAsString(req);
|
|
||||||
// 3. 构建请求
|
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(API_URL + "/contents/generations/tasks/" + id)
|
.url(volcBaseUrl + "/api/v3/contents/generations/tasks/" + id)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.header("Authorization", "Bearer " + apiKey)
|
.header("Authorization", "Bearer " + volcApiKey)
|
||||||
.get()
|
.get()
|
||||||
.build();
|
.build();
|
||||||
// 4. 发送同步请求(因方法需要返回值,使用execute而非enqueue)
|
|
||||||
Response response = OkHttpUtils.newCall(request).execute();
|
Response response = OkHttpUtils.newCall(request).execute();
|
||||||
// 5. 处理响应
|
|
||||||
if (!response.isSuccessful()) {
|
if (!response.isSuccessful()) {
|
||||||
// 非200状态:返回错误信息(假设ByteBodyRes有error字段)
|
|
||||||
String errorMsg = response.body() != null ? response.body().string() : "uploadVideo error";
|
String errorMsg = response.body() != null ? response.body().string() : "uploadVideo error";
|
||||||
throw new Exception("uploadVideo error:" + errorMsg);
|
throw new Exception("uploadVideo error:" + errorMsg);
|
||||||
}
|
}
|
||||||
// 6. 解析成功响应为ByteBodyRes
|
|
||||||
if (response.body() == null) {
|
if (response.body() == null) {
|
||||||
throw new Exception("uploadVideo response null");
|
throw new Exception("uploadVideo response null");
|
||||||
}
|
}
|
||||||
|
|
||||||
String responseBody = response.body().string();
|
String responseBody = response.body().string();
|
||||||
return objectMapper.readValue(responseBody, ByteBodyRes.class);
|
return objectMapper.readValue(responseBody, ByteBodyRes.class);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue