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.domain.AjaxResult;
|
||||
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.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.TencentCosUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -36,13 +36,21 @@ import java.util.regex.Pattern;
|
|||
public class ByteApiController extends BaseController {
|
||||
|
||||
private final IByteService byteService;
|
||||
private final AwsS3Util awsS3Util;
|
||||
private final TencentCosUtil tencentCosUtil;
|
||||
private final IAiOrderService aiOrderService;
|
||||
private final IAiManagerService managerService;
|
||||
private final IAiTagService aiTagService;
|
||||
@Value("${byteapi.callBackUrl}")
|
||||
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")
|
||||
@ApiOperation("文生图")
|
||||
public AjaxResult promptToImg(@RequestBody ByteApiRequest request) {
|
||||
|
|
@ -85,7 +93,9 @@ public class ByteApiController extends BaseController {
|
|||
}
|
||||
aiOrder.setText(text);
|
||||
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.setSequential_image_generation("disabled");
|
||||
byteBodyReq.setResponse_format("url");
|
||||
|
|
@ -96,7 +106,7 @@ public class ByteApiController extends BaseController {
|
|||
List<ByteDataRes> data = byteBodyRes.getData();
|
||||
ByteDataRes byteDataRes = data.get(0);
|
||||
String url = byteDataRes.getUrl();
|
||||
url = awsS3Util.uploadFileByUrl(url);
|
||||
url = tencentCosUtil.uploadFileByUrl(url);
|
||||
if (url == null) {
|
||||
// 判断生成失败,退回金额逻辑
|
||||
aiOrderService.orderFailure(aiOrder);
|
||||
|
|
@ -175,7 +185,7 @@ public class ByteApiController extends BaseController {
|
|||
List<ByteDataRes> data = byteBodyRes.getData();
|
||||
ByteDataRes byteDataRes = data.get(0);
|
||||
String url = byteDataRes.getUrl();
|
||||
url = awsS3Util.uploadFileByUrl(url);
|
||||
url = tencentCosUtil.uploadFileByUrl(url);
|
||||
if (url == null) {
|
||||
// 判断生成失败,退回金额逻辑
|
||||
aiOrderService.orderFailure(aiOrder);
|
||||
|
|
@ -191,7 +201,7 @@ public class ByteApiController extends BaseController {
|
|||
}
|
||||
|
||||
@PostMapping("/imgToVideo")
|
||||
@ApiOperation("图生视频")
|
||||
@ApiOperation("图生视频 (Seedance 2.0)")
|
||||
public AjaxResult imgToVideo(@RequestBody ByteApiRequest request) throws Exception {
|
||||
String functionType = request.getFunctionType();
|
||||
if (null == functionType) {
|
||||
|
|
@ -235,51 +245,56 @@ public class ByteApiController extends BaseController {
|
|||
return AjaxResult.error(-1, "You have a low balance, please recharge");
|
||||
}
|
||||
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.setImg1(firstUrl.toString());
|
||||
Integer duration = request.getDuration();
|
||||
|
||||
Integer duration = request.getDuration() != null ? request.getDuration() : 4;
|
||||
|
||||
ByteBodyReq byteBodyReq = new ByteBodyReq();
|
||||
byteBodyReq.setModel("ep-20251113072240-cfxlz");
|
||||
byteBodyReq.setCallback_url(url + "/api/ai/callBack");
|
||||
List<ContentItem> content = new ArrayList<>();
|
||||
ContentItem contentItem = new ContentItem();
|
||||
contentItem.setType("text");
|
||||
contentItem.setText(text + " --dur " + duration + " --fps 24 --rs 720p --wm false --cf false");
|
||||
content.add(contentItem);
|
||||
// model由前端传入,默认为Seedance2.0
|
||||
byteBodyReq.setModel(StringUtils.isNotEmpty(request.getModel()) ?
|
||||
request.getModel() : "ep-20260326165811-dlkth");
|
||||
byteBodyReq.setCallback_url(volcCallbackUrl);
|
||||
|
||||
ContentItem contentItem1 = new ContentItem();
|
||||
contentItem1.setType("image_url");
|
||||
contentItem1.setRole("first_frame");
|
||||
ImageUrl imageUrl1 = new ImageUrl();
|
||||
imageUrl1.setUrl(firstUrl.toString());
|
||||
contentItem1.setImageUrl(imageUrl1);
|
||||
content.add(contentItem1);
|
||||
// 构建符合火山引擎格式的content
|
||||
List<ContentItem> contentList = new ArrayList<>();
|
||||
|
||||
// 文本提示词
|
||||
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();
|
||||
if (StringUtils.isNotBlank(lastUrl)) {
|
||||
ContentItem contentItem2 = new ContentItem();
|
||||
contentItem2.setType("image_url");
|
||||
contentItem2.setRole("last_frame");
|
||||
ImageUrl imageUrl2 = new ImageUrl();
|
||||
imageUrl2.setUrl(lastUrl);
|
||||
contentItem2.setImageUrl(imageUrl2);
|
||||
content.add(contentItem2);
|
||||
ContentItem lastFrameItem = new ContentItem();
|
||||
lastFrameItem.setType("image_url");
|
||||
lastFrameItem.setRole("last_frame");
|
||||
ImageUrl lastImageUrl = new ImageUrl();
|
||||
lastImageUrl.setUrl(lastUrl);
|
||||
lastFrameItem.setImageUrl(lastImageUrl);
|
||||
contentList.add(lastFrameItem);
|
||||
aiOrder.setImg2(lastUrl);
|
||||
}
|
||||
byteBodyReq.setContent(content);
|
||||
|
||||
byteBodyReq.setContent(contentList);
|
||||
byteBodyReq.setDuration(duration);
|
||||
byteBodyReq.setResolution("720p");
|
||||
byteBodyReq.setRatio("3:4");
|
||||
|
||||
ByteBodyRes byteBodyRes = byteService.imgToVideo(byteBodyReq);
|
||||
String id = byteBodyRes.getId();
|
||||
if (id == null) {
|
||||
// 判断生成失败,退回金额逻辑
|
||||
aiOrderService.orderFailure(aiOrder);
|
||||
return AjaxResult.error(-2, "generation failed, balance has been refunded");
|
||||
}
|
||||
|
|
@ -299,7 +314,7 @@ public class ByteApiController extends BaseController {
|
|||
if ("succeeded".equals(byteBodyRes.getStatus())) {
|
||||
content content = byteBodyRes.getContent();
|
||||
String videoUrl = content.getVideo_url();
|
||||
videoUrl = awsS3Util.uploadFileByUrl(videoUrl);
|
||||
videoUrl = tencentCosUtil.uploadFileByUrl(videoUrl);
|
||||
content.setVideo_url(videoUrl);
|
||||
AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(id);
|
||||
AiOrder aiOrder = new AiOrder();
|
||||
|
|
@ -311,24 +326,28 @@ public class ByteApiController extends BaseController {
|
|||
return AjaxResult.success(byteBodyRes);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/callBack")
|
||||
@ApiOperation("视频下载回调")
|
||||
@GetMapping(value = "/volcCallback")
|
||||
@ApiOperation("火山引擎视频回调")
|
||||
@Anonymous
|
||||
public AjaxResult callBack(@PathVariable("id") ByteBodyRes byteBodyRes) throws Exception {
|
||||
public AjaxResult volcCallback(@RequestBody ByteBodyRes byteBodyRes) throws Exception {
|
||||
if ("succeeded".equals(byteBodyRes.getStatus())) {
|
||||
String id = byteBodyRes.getId();
|
||||
content content = byteBodyRes.getContent();
|
||||
String videoUrl = content.getVideo_url();
|
||||
videoUrl = awsS3Util.uploadFileByUrl(videoUrl);
|
||||
content.setVideo_url(videoUrl);
|
||||
content contentObj = byteBodyRes.getContent();
|
||||
if (contentObj != null && StringUtils.isNotEmpty(contentObj.getVideo_url())) {
|
||||
String videoUrl = contentObj.getVideo_url();
|
||||
videoUrl = tencentCosUtil.uploadFileByUrl(videoUrl);
|
||||
contentObj.setVideo_url(videoUrl);
|
||||
|
||||
AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(id);
|
||||
if (aiOrderByResult != null) {
|
||||
AiOrder aiOrder = new AiOrder();
|
||||
aiOrder.setId(aiOrderByResult.getId());
|
||||
aiOrder.setResult(videoUrl);
|
||||
// aiOrder.setUpdateBy(SecurityUtils.getLoginAiUser().getUsername());
|
||||
aiOrderService.updateAiOrder(aiOrder);
|
||||
}
|
||||
return AjaxResult.success(byteBodyRes);
|
||||
}
|
||||
}
|
||||
return AjaxResult.success("callback success");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.ruoyi.api;
|
||||
|
||||
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.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
|
|
@ -18,7 +18,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
@Api(tags = "文件上传")
|
||||
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
||||
public class FileController {
|
||||
private final AwsS3Util awsS3Util;
|
||||
private final TencentCosUtil tencentCosUtil;
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
|
|
@ -30,7 +30,7 @@ public class FileController {
|
|||
@ApiParam(name = "file", value = "文件", required = true)
|
||||
@RequestParam("file") MultipartFile file) throws Exception {
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
String uploadUrl = awsS3Util.uploadMultipartFile(file, true);
|
||||
String uploadUrl = tencentCosUtil.uploadMultipartFile(file, true);
|
||||
ajax.put("url", uploadUrl);
|
||||
return ajax;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ public class ByteApiRequest {
|
|||
@ApiModelProperty(name = "标签字符串")
|
||||
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.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.exception.base.BaseException;
|
||||
import com.ruoyi.common.utils.AwsS3Util;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.TencentCosUtil;
|
||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||
|
|
@ -36,7 +35,6 @@ public class CommonController {
|
|||
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
|
||||
private final ServerConfig serverConfig;
|
||||
private final TencentCosUtil tencentCosUtil;
|
||||
private final AwsS3Util awsS3Util;
|
||||
|
||||
/**
|
||||
* 通用下载请求
|
||||
|
|
@ -158,7 +156,7 @@ public class CommonController {
|
|||
}
|
||||
|
||||
/**
|
||||
* AWS上传请求(单个)
|
||||
* 腾讯云COS上传请求(单个)
|
||||
*/
|
||||
@ApiOperation("图片上传接口")
|
||||
@PostMapping("/aws/upload")
|
||||
|
|
@ -166,7 +164,7 @@ public class CommonController {
|
|||
AjaxResult ajax = AjaxResult.success();
|
||||
String uploadUrl;
|
||||
try {
|
||||
uploadUrl = awsS3Util.uploadMultipartFile(file, true);
|
||||
uploadUrl = tencentCosUtil.uploadMultipartFile(file, true);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,24 +209,32 @@ google:
|
|||
redirect-uri:
|
||||
|
||||
tencentCos:
|
||||
accessKey:
|
||||
secretKey:
|
||||
endpoint:
|
||||
bucketName:
|
||||
domain:
|
||||
accessKey: ${TENCENT_COS_SECRET_ID:}
|
||||
secretKey: ${TENCENT_COS_SECRET_KEY:}
|
||||
endpoint: ap-guangzhou
|
||||
bucketName: seedance-1331490964
|
||||
domain: https://seedance-1331490964.cos.ap-guangzhou.myqcloud.com
|
||||
|
||||
aws:
|
||||
accessKey: AKIAYVMHEVDDZQGE3HVX
|
||||
secretKey: B9nxdferMhdRuxzoKeQam/NxiVvIhI7lSru6VfwG
|
||||
endpoint: ap-southeast-1
|
||||
bucketName: di-image
|
||||
domain: https://images.iqyjsnwv.com/
|
||||
# aws配置已替换为腾讯云COS,请在环境变量或配置文件中设置腾讯云凭证
|
||||
# aws:
|
||||
# accessKey: AKIAYVMHEVDDZQGE3HVX
|
||||
# secretKey: B9nxdferMhdRuxzoKeQam/NxiVvIhI7lSru6VfwG
|
||||
# endpoint: ap-southeast-1
|
||||
# bucketName: di-image
|
||||
# domain: https://images.iqyjsnwv.com/
|
||||
|
||||
byteapi:
|
||||
url: https://ark.ap-southeast.bytepluses.com/api/v3
|
||||
apiKey: 327d2815-2516-44c2-9e32-2dc50bf7afd7
|
||||
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:
|
||||
url: https://api.jinshapay.xyz
|
||||
appId: 1763617360
|
||||
|
|
|
|||
|
|
@ -13,7 +13,13 @@ import org.springframework.beans.factory.annotation.Value;
|
|||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
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;
|
||||
|
||||
@Component
|
||||
|
|
@ -33,22 +39,32 @@ public class TencentCosUtil {
|
|||
private String domain;
|
||||
|
||||
|
||||
//文件上传
|
||||
/**
|
||||
* 上传MultipartFile到腾讯云COS,返回文件访问地址
|
||||
* 与AwsS3Util.uploadMultipartFile方法接口兼容
|
||||
*/
|
||||
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();
|
||||
|
||||
// 存储桶的命名格式为 BucketName-APPID,此处填写的存储桶名称必须为此格式
|
||||
// 对象键(Key)是对象在存储桶中的唯一标识。 998u-09iu-09i-333
|
||||
//在文件名称前面添加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;
|
||||
// 生成唯一文件键,格式与AWS一致:yyyy/MM/dd/uuid_filename
|
||||
String key = generateCosKey(file.getOriginalFilename());
|
||||
|
||||
try {
|
||||
//获取上传文件输入流
|
||||
InputStream inputStream = file.getInputStream();
|
||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(
|
||||
|
|
@ -56,17 +72,41 @@ public class TencentCosUtil {
|
|||
key,
|
||||
inputStream,
|
||||
objectMetadata);
|
||||
// 高级接口会返回一个异步结果Upload
|
||||
|
||||
PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
|
||||
|
||||
//返回上传文件路径
|
||||
//https://ggkt-atguigu-1310644373.cos.ap-beijing.myqcloud.com/01.jpg
|
||||
String url = domain + "/" + key;
|
||||
// 返回COS文件访问地址
|
||||
String url = domain + (domain.endsWith("/") ? "" : "/") + key;
|
||||
return url;
|
||||
} catch (Exception e) {
|
||||
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);
|
||||
// 这里建议设置使用 https 协议
|
||||
clientConfig.setHttpProtocol(HttpProtocol.https);
|
||||
clientConfig.setConnectionTimeout(30 * 1000); // 连接超时30秒
|
||||
clientConfig.setSocketTimeout(60 * 1000); // 读取超时60秒
|
||||
//1.3 生成cos客户端
|
||||
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")
|
||||
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}")
|
||||
private String apiKey;
|
||||
|
||||
// 火山引擎配置
|
||||
@Value("${volcengine.ark.baseUrl:https://ark.cn-beijing.volces.com}")
|
||||
private String volcBaseUrl;
|
||||
|
||||
@Value("${volcengine.ark.apiKey}")
|
||||
private String volcApiKey;
|
||||
|
||||
@Override
|
||||
public ByteBodyRes promptToImg(ByteBodyReq req) throws Exception {
|
||||
return this.imgToImg(req);
|
||||
|
|
@ -75,70 +82,63 @@ public class ByteService implements IByteService {
|
|||
|
||||
@Override
|
||||
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);
|
||||
// 3. 构建请求
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(API_URL + "/contents/generations/tasks")
|
||||
.url(volcBaseUrl + "/api/v3/contents/generations/tasks")
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", "Bearer " + apiKey)
|
||||
.header("Authorization", "Bearer " + volcApiKey)
|
||||
.post(RequestBody.create(
|
||||
MediaType.parse("application/json; charset=utf-8"),
|
||||
jsonBody
|
||||
))
|
||||
.build();
|
||||
// 4. 发送同步请求(因方法需要返回值,使用execute而非enqueue)
|
||||
|
||||
Response response = OkHttpUtils.newCall(request).execute();
|
||||
// 5. 处理响应
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
// 非200状态:返回错误信息(假设ByteBodyRes有error字段)
|
||||
String errorMsg = response.body() != null ? response.body().string() : "imgToVideo error";
|
||||
throw new Exception("imgToVideo error:" + errorMsg);
|
||||
}
|
||||
// 6. 解析成功响应为ByteBodyRes
|
||||
|
||||
if (response.body() == null) {
|
||||
throw new Exception("imgToVideo response null");
|
||||
}
|
||||
|
||||
String responseBody = response.body().string();
|
||||
return objectMapper.readValue(responseBody, ByteBodyRes.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBodyRes uploadVideo(String id) throws Exception {
|
||||
// 1. 验证请求参数(可选,根据业务需求)
|
||||
if (StringUtils.isBlank(id)) {
|
||||
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()
|
||||
.url(API_URL + "/contents/generations/tasks/" + id)
|
||||
.url(volcBaseUrl + "/api/v3/contents/generations/tasks/" + id)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", "Bearer " + apiKey)
|
||||
.header("Authorization", "Bearer " + volcApiKey)
|
||||
.get()
|
||||
.build();
|
||||
// 4. 发送同步请求(因方法需要返回值,使用execute而非enqueue)
|
||||
|
||||
Response response = OkHttpUtils.newCall(request).execute();
|
||||
// 5. 处理响应
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
// 非200状态:返回错误信息(假设ByteBodyRes有error字段)
|
||||
String errorMsg = response.body() != null ? response.body().string() : "uploadVideo error";
|
||||
throw new Exception("uploadVideo error:" + errorMsg);
|
||||
}
|
||||
// 6. 解析成功响应为ByteBodyRes
|
||||
|
||||
if (response.body() == null) {
|
||||
throw new Exception("uploadVideo response null");
|
||||
}
|
||||
|
||||
String responseBody = response.body().string();
|
||||
return objectMapper.readValue(responseBody, ByteBodyRes.class);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue