Spring-ai-alibaba视频生成

文章目录

Spring AI Alibaba 视频模型实战(文生视频 / 图生视频)

用 Spring AI Alibaba 的 DashScopeVideoModel 完成文生视频、多图参考合成、本地图生视频。对应模块 spring-video-demo

版本

  • Spring Boot:3.4.5
  • spring-ai-alibaba-starter-dashscope:1.1.2.1
  • Java:17

Spring-ai更新速度快,请以官方为准

可参考网址:

一、依赖引入

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    <version>1.1.2.1</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dashscope-sdk-java</artifactId>
    <version>2.22.18</version>
</dependency>

二、yml 配置

yaml 复制代码
spring:
  application:
    name: spring-video-demo
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
      video:
        options:
          # 默认文生视频;接口里可覆盖为 wanx2.1-vace-plus / wanx2.1-i2v-turbo 等
          model: wan2.6-t2v

三、代码案例:文生视频

java 复制代码
@RestController
@RequestMapping("/ai/video")
public class Text2Video {

    private final DashScopeVideoModel videoModel;

    public Text2Video(DashScopeVideoModel videoModel) {
        this.videoModel = videoModel;
    }

    /**
     * GET /ai/video/text2video?prompt=...
     * call() 内部会提交异步任务并轮询,直到成功或失败。
     */
    @GetMapping("/text2video")
    public String text2Video(@RequestParam(defaultValue = "一只猫在草地上奔跑") String prompt) {

        // 1)组装模型、输入、输出参数
        DashScopeVideoOptions options = DashScopeVideoOptions.builder()
                .model(DashScopeModel.VideoModel.WAN26_T2V.getName()) // wan2.6-t2v
                .input(DashScopeVideoOptions.InputOptions.builder()
                        .prompt(prompt)           // 正向提示词(可写分镜)
                        .negativePrompt("")       // 反向提示词:不想出现的内容
                        .build())
                .parameters(DashScopeVideoOptions.ParametersOptions.builder()
                        .size("1280*720")         // 分辨率 宽*高
                        .shotType("multi")        // 多镜头
                        .duration(10)             // 时长(秒)
                        .promptExtend(true)       // 提示词智能扩写
                        .seed(12345L)             // 随机种子,便于复现
                        .build())
                .build();

        // 2)Prompt = 正文 + Options
        VideoPrompt videoPrompt = VideoPrompt.builder()
                .content(prompt)
                .options(options)
                .build();

        // 3)同步等待生成完成
        VideoResponse response = videoModel.call(videoPrompt);
        DashScopeVideoResponse.VideoOutput output = response.getResult().getOutput();

        if (output == null) {
            return "生成失败: 无 output";
        }
        if (output.videoUrl() != null && !output.videoUrl().isBlank()) {
            // 立刻下载到本地 video/,避免 OSS 签名 URL 过期后 AccessDenied
            return VideoFileHelper.successMessage(output.videoUrl(), output.taskId(), output.taskStatus());
        }
        return "生成未完成或失败: status=" + output.taskStatus()
                + ", taskId=" + output.taskId();
    }
}

四、代码案例:多图参考合成

java 复制代码
@RestController
@RequestMapping("/ai/video")
public class VideoSynthesis {

    private final DashScopeVideoModel videoModel;

    // 官方示例参考图(公网可访问)
    private static final List<String> DEFAULT_REF_IMAGES = List.of(
            "http://wanx.alicdn.com/material/20250318/image_reference_2_5_16.png",
            "http://wanx.alicdn.com/material/20250318/image_reference_1_5_16.png"
    );

    public VideoSynthesis(DashScopeVideoModel videoModel) {
        this.videoModel = videoModel;
    }

    /**
     * GET /ai/video/synthesis?prompt=...
     * 功能:image_reference(多图参考 → 视频)
     */
    @GetMapping("/synthesis")
    public String synthesis(@RequestParam(defaultValue = "女孩从雾气森林中走出") String prompt) {

        DashScopeVideoOptions options = DashScopeVideoOptions.builder()
                .model(DashScopeModel.VideoModel.WANX21_VACE_PLUS.getName()) // wanx2.1-vace-plus
                .input(DashScopeVideoOptions.InputOptions.builder()
                        .function("image_reference")   // 功能类型:多图参考
                        .prompt(prompt)
                        .refImagesUrl(DEFAULT_REF_IMAGES) // 参考图 URL 列表
                        .build())
                .parameters(DashScopeVideoOptions.ParametersOptions.builder()
                        .promptExtend(true)
                        // 与参考图顺序对应:第一张主体,第二张背景
                        .objOrBg(List.of("obj", "bg"))
                        .size("1280*720")
                        .build())
                .build();

        VideoPrompt videoPrompt = VideoPrompt.builder()
                .content(prompt)
                .options(options)
                .build();

        VideoResponse response = videoModel.call(videoPrompt);
        DashScopeVideoResponse.VideoOutput output = response.getResult().getOutput();

        if (output != null && output.videoUrl() != null && !output.videoUrl().isBlank()) {
            return VideoFileHelper.successMessage(output.videoUrl(), output.taskId(), output.taskStatus());
        }
        return "生成失败或未完成";
    }
}

五、代码案例:本地图片 → 图生视频

云端读不到你电脑上的路径,所以把 MultipartFile 转成 Base64 data URI。

java 复制代码
/**
 * POST /ai/video/image2video
 * form-data:file=本地图片,prompt=可选运动描述
 */
@PostMapping("/image2video")
public String image2Video(
        @RequestParam("file") MultipartFile file,
        @RequestParam(defaultValue = "图片中的主体轻轻走动,镜头缓慢推进") String prompt)
        throws IOException {

    if (file == null || file.isEmpty()) {
        return "请上传图片,表单字段名: file";
    }

    // MultipartFile → data:image/png;base64,xxxx
    String imgUrl = toImageDataUri(file);

    DashScopeVideoOptions options = DashScopeVideoOptions.builder()
            .model(DashScopeModel.VideoModel.WANX21_I2V_TURBO.getName()) // wanx2.1-i2v-turbo
            .input(DashScopeVideoOptions.InputOptions.builder()
                    .prompt(prompt)
                    .imgUrl(imgUrl) // 首帧:公网 URL 或 data URI
                    .build())
            .parameters(DashScopeVideoOptions.ParametersOptions.builder()
                    .size("1280*720")
                    .promptExtend(true)
                    .duration(5)
                    .build())
            .build();

    VideoResponse response = videoModel.call(
            VideoPrompt.builder().content(prompt).options(options).build());
    DashScopeVideoResponse.VideoOutput output = response.getResult().getOutput();

    if (output != null && output.videoUrl() != null && !output.videoUrl().isBlank()) {
        return VideoFileHelper.successMessage(output.videoUrl(), output.taskId(), output.taskStatus());
    }
    return "生成失败或未完成";
}

/** 上传图片 → data:{mime};base64,{data} */
private static String toImageDataUri(MultipartFile file) throws IOException {
    String mime = file.getContentType();
    if (mime == null || !mime.startsWith("image/")) {
        mime = "image/png";
    }
    String base64 = Base64.getEncoder().encodeToString(file.getBytes());
    return "data:" + mime + ";base64," + base64;
}

六、临时 URL 立刻落盘

返回的 OSS 地址带 Expires / Signature,过期或截断后会出现 AccessDenied。生成成功后立刻下载:

java 复制代码
public final class VideoFileHelper {

    /** 运行目录下的 video/ */
    public static final Path VIDEO_DIR = Paths.get("video");

    /** 从签名 URL 下载 mp4;成功返回本地绝对路径 */
    public static String download(String videoUrl, String taskId) {
        try {
            Files.createDirectories(VIDEO_DIR);
            Path path = VIDEO_DIR.resolve("video-" + taskId + ".mp4");
            try (InputStream in = URI.create(videoUrl).toURL().openStream()) {
                Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
            }
            return path.toAbsolutePath().toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String successMessage(String videoUrl, String taskId, String status) {
        String local = download(videoUrl, taskId);
        return "视频 URL: " + videoUrl
                + "\ntaskId: " + taskId
                + "\nstatus: " + status
                + (local != null ? "\n本地文件: " + local : "\n本地下载失败");
    }
}
相关推荐
Luhui Dev1 小时前
几何题配图 API 怎么选?从文本生成到可编辑源文件的接入指南
人工智能·数学·agent·luhuidev
程序员老陆1 小时前
FFmpeg6 在 Windows 打开麦克风并录成 PCM:不走 Qt Multimedia,也不碰 WASAPI
windows·ffmpeg·音视频·pcm
Canace1 小时前
为了给视频里的人脸打码,我让 Claude 做了个 Skill
前端·人工智能
武子康1 小时前
Prompt 之外,生产级 Agent Harness 到底在控制什么
人工智能·llm·agent
ltqvibe1 小时前
企业智能问数落地:从一句话到跨系统完整答案
java·人工智能·本体语义平台
懂好多的工业电源工程师1 小时前
F2424S-1WR3 适配优选 钡特电源 DF1-24S24LS|1W 工业24V转24V模块电源参数技术性能解析
网络·人工智能·电源模块
会编程的吕洞宾1 小时前
Spring Boot 虚拟线程实战:从原理到生产环境
java·后端·spring
计算机魔术师1 小时前
Replit Design 发布:AI 赋能设计愿景
人工智能·ai编程·编程语言