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本地下载失败");
    }
}
相关推荐
明月_清风13 分钟前
Muse 登顶 App Store 第一,SDK 直接开源:AI Agent 开始进入下一个阶段
人工智能·后端
JackSparrow41440 分钟前
和AI一起将全部CSDN博文迁移到个人博客站
人工智能·程序人生·ai·github·cloudflare·astro·静态博客
55873 生态系统1 小时前
第 22 篇|社区聊天|55873 文明共建者的日常交流与协作界面
人工智能·区块链·55873全域文明生态体系·55873操作系统·55873社区聊天
搬砖的小码农_Sky1 小时前
AI Agent:如何处理Claude Code 最近版本(2026年更新)引入的模型上下文限制
人工智能·windows·ai·ai编程
企业数字化笔记1 小时前
视频目标跟踪怎么选?SORT、DeepSORT、ByteTrack 的连续性、遮挡与计算成本对比
人工智能·目标跟踪·音视频
weixin_307779131 小时前
有限产能智能排产与动态重排智能体:从需求解构到技术实现
开发语言·人工智能·算法·架构
Ivanqhz1 小时前
激活函数在 Transformer 中的作用及各种变体简述
java·linux·数据库·人工智能·深度学习
染指11101 小时前
134.Agent-多Agent框架-LangChain多智能体
人工智能·中间件·langchain·agents
fkyyly2 小时前
企业 Agent 落地的两场战争:内部经营闭环与 ToB 外部规模赋能
人工智能·codeagent
看浪的路人2 小时前
第9讲:AI 应用混沌工程与容灾演练
人工智能