fix: 新需求

This commit is contained in:
old burden 2026-03-27 10:51:32 +08:00
parent 06fb0de039
commit 3a05e41f79
8 changed files with 285 additions and 113 deletions

View File

@ -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");
} }
} }

View File

@ -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;
} }

View File

@ -31,6 +31,8 @@ public class ByteApiRequest {
@ApiModelProperty(name = "标签字符串") @ApiModelProperty(name = "标签字符串")
private String tags; private String tags;
@ApiModelProperty(name = "使用的模型")
private String model;
} }

View File

@ -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());
} }

View File

@ -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: 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 # accessKey: AKIAYVMHEVDDZQGE3HVX
endpoint: ap-southeast-1 # secretKey: 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
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

View File

@ -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());
}
};
}
} }

View File

@ -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;
} }

View File

@ -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 errorreq is null");
}
// 1. 验证请求参数可选根据业务需求 // 使用火山引擎配置
// if (StringUtils.isBlank(req.getPrompt())) {
// throw new Exception("imgToVideo errorprompt is null");
// }
// 2. 构建请求体JSON基于ByteBodyReq的字段
// 注意ByteBodyReq需包含与API参数对应的字段modelprompt等
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 errorid is null"); throw new Exception("uploadVideo errorid is null");
} }
// 2. 构建请求体JSON基于ByteBodyReq的字段 // 使用火山引擎配置查询任务状态
// 注意ByteBodyReq需包含与API参数对应的字段modelprompt等
//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);
} }