通过巨量引擎API获取视频URL并上传到连山云对象存储

楠笙
2025-11-01
点 赞
0
热 度
28
评 论
0

文章摘要

智阅GPT

背景

在使用 巨量引擎 时,我们可能会通过 API 获取到视频的 URL。但是,获取到的视频 URL 通常是 前端临时链接,不能直接用于下载。为了能够下载视频并上传到自己的存储系统,我们需要绕过这个临时链接的限制。

通过分析浏览器的开发者工具(F12),我们发现,虽然视频 URL 是临时的,但它背后存在一个 302 重定向,通过该重定向可以获取到一个有效的、可以直接下载的视频地址。接下来,我们将介绍如何利用这一点来下载视频,并将其上传到 连山 的对象存储。

获取视频的临时链接

首先,我们使用 巨量引擎 API 中的接口,来请求获取视频的 URL。巨量引擎提供了一个接口,能够返回广告视频的相关信息,其中包括视频的 URL。具体接口如下:

该接口返回的数据中,包含了一个临时的 video_url,该链接指向的视频是临时的,因此不能直接下载。

返回的链接类似于:

https://cc.oceanengine.com/anm/CsoDcnZJc1URoiNJCoy7D5Md3NhcNhzAR2gSeMz1-fVCMuR16p1n0gbn5dYUIeWkgXE1zT75mX7Ianl0f0d2msKxKeJH5oACQM2Hn1ixIt2vhQXp0qYQQfSDb9796kZ2TE47Q_RRlpzsb8trRgW2TO1dO6FeD7tk6YdNlD1RXUZ9dX5xTxUkdAaJbB7TCJwHQw2ziUi8p5CR7rPLqZuqVLmtpYSUpVK5M0z3oLqKuU23zRKbrJfa179cUvaZllyZauzlmWUZoJR8CCvRDMreWJkAOte2rDjBUuQBVMTUMiDJLfb1wEVznDnkZk3AYebXG9aQo2sDwf-AJCpIS7I2zRjqXwrLlMbdYNs2Mw9HNY8xKG2wo7v_4oUAo1QPvWUI-nXfF_oh4c9XJRnOnps2rFdm-2oWak43CeeVH66eYGtBtgC0iO6XPFRR0j_zn6V5xgskuKZlNDfdOB-Fju36-L5PdMTydbJbp97SR09Adjnqz35m-qhSP6yOMsg-jglnT9phjY8hU0pBMVnFuuaPIAAoHlkPFa0uaBYe9CjLmQ1yl2T6s7bBKmlAasSVkX7NIWPGJyE7bQUD8B4fdzIyJ93jM2FEwKP567BQLzMaSwo8AAAAAAAAAAAAAE-oNyT7JAzSaiEZbzi1UJv_nlcIyiTUjexlRbi_pBsLgx4aQa9DDhULVrie-fa66ynlEMesgA4Y7fL_4QEgASIBA5x2FaU=

是一个前端地址,而且还是加密的,临时的,每次请求都不一样

分析 F12 重定向过程

访问此链接,使用浏览器的开发者工具(F12),我们发现,当我们请求这个临时视频 URL 时,会首先返回一个 302 Redirect 响应。

在响应中,我们看到以下关键内容:

  • Set-Cookie:返回了视频请求所需的 Cookie

  • Location:重定向到一个新的 URL,这个 URL 是实际的视频地址。

从这个过程我们可以了解到,虽然初始的 URL 是临时的,但它会通过重定向获取到一个永久的视频下载地址。

于是我便怀疑这个set-cookie是访问视频的关键信息,因为最开始时访问会被直接返回一个403。

解决方案:解析重定向并下载视频

为了下载视频,我们需要模拟这个过程,首先发送请求到临时的 URL,获取重定向信息(特别是 Set-CookieLocation),然后通过这些信息发送请求到最终的视频 URL,成功获取视频文件。

关键代码实现

以下是使用 OkHttp 发送请求并解析视频下载地址的代码:

import okhttp3.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

public class VideoDownloader {

    private final OkHttpClient sharedClient = new OkHttpClient.Builder()
            .connectionPool(new ConnectionPool(150, 30, TimeUnit.MINUTES))
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)) //优先 HTTP/2
            .followRedirects(false)  // 禁用自动重定向
            .build();

    public InputStream downloadFromRealUrl(String initialUrl) throws IOException {
        // 第一步:发送请求获取 302 重定向
        Request initialRequest = new Request.Builder()
                .url(initialUrl)
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
                .header("Accept", "video/webm,video/ogg,video/*;q=0.9")
                .header("Referer", "https://cc.oceanengine.com/")
                .header("Origin", "https://cc.oceanengine.com/")
                .build();

        // 执行请求
        Response initialResponse = sharedClient.newCall(initialRequest).execute();

        // 处理 302 响应
        if (initialResponse.code() == 302) {
            // 提取 Set-Cookie 和 Location
            String setCookie = initialResponse.header("Set-Cookie");
            String location = initialResponse.header("Location");

            if (setCookie == null || location == null) {
                throw new RuntimeException("响应缺少必要的 Set-Cookie 或 Location");
            }

            // 第三步:提取到的 cookies 在后续请求中使用
            // 创建一个新的请求到重定向的 URL
            Request downloadRequest = new Request.Builder()
                    .url(location)
                    .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
                    .header("Accept", "video/webm,video/ogg,video/*;q=0.9")
                    .header("Referer", "https://cc.oceanengine.com/")
                    .header("Origin", "https://cc.oceanengine.com/")
                    .header("Cookie", setCookie) // 设置从 302 响应中获取的 cookies
                    .build();

            // 执行下载请求
            Response downloadResponse = sharedClient.newCall(downloadRequest).execute();

            if (!downloadResponse.isSuccessful()) {
                throw new RuntimeException("视频下载失败: HTTP " + downloadResponse.code());
            }

            // 返回视频流
            return downloadResponse.body().byteStream();
        } else {
            throw new RuntimeException("初始请求失败: HTTP " + initialResponse.code());
        }
    }

}

代码解析

  • 禁用自动重定向:我们禁用了 followRedirects(false),这样可以手动处理 302 重定向。

  • 获取 Set-Cookie 和 Location:从初次响应中提取 Set-Cookie(存储了视频下载所需的 Cookie)和 Location(视频重定向 URL)。

  • 通过 Cookies 请求最终视频地址:使用从初始请求中获得的 Set-Cookie,然后发送请求到 Location 指向的最终视频地址,成功获取视频流。

上传到连山云

通过这种方式,我们成功使用Java解析了视频文件,并可以将其上传到 连山 对象存储桶,因为巨量引擎的上传视频素材只接受连山内网视频地址,因此我们可以直接上传视频流到指定的存储桶,并获取到内网地址,上传视频素材到指定账户上。

 // 对象名,在连山云 TOS 中保存的路径(建议使用英文/数字,避免中文)
                        String objectKey = "video/" + newFileName;

                        try (
                                TOSV2 tos = new TOSV2ClientBuilder().build(REGION, ENDPOINT, ACCESS_KEY, SECRET_KEY);
                                InputStream inputStream = downloadFromUrl(videoDownUrl) // 使用增强方法
                        ) {
                            PutObjectInput putObjectInput = new PutObjectInput()
                                    .setBucket(BUCKET_NAME)
                                    .setKey(objectKey)
                                    .setContent(inputStream);

                            PutObjectOutput output = tos.putObject(putObjectInput);

                            // 构造 TOS 公网访问地址
                            tosUrl = String.format(
                                    "https://%s.tos-%s.ivolces.com/%s",
                                    BUCKET_NAME, REGION, objectKey
                            );

                            log.info("视频上传成功!TOS URL: {}", tosUrl);
                            log.info("ETag: {}, CRC64: {}", output.getEtag(), output.getHashCrc64ecma());

                        } catch (IOException e) {
                            log.error("读取视频URL失败,无法下载文件。URL={}", videoDownUrl, e);
                            record.setResult("读取视频URL失败,无法下载文件:" + e.getMessage());
                            return record;
                        } catch (TosClientException e) {
                            log.error("TOS客户端异常,请求未发送。Bucket={}, Key={}", BUCKET_NAME, objectKey, e);
                            record.setResult("TOS客户端异常,请求未发送:" + e.getMessage());
                            return record;
                        } catch (TosServerException e) {
                            log.error("TOS服务端返回错误。StatusCode={}, Code={}, Message={}",
                                    e.getStatusCode(), e.getCode(), e.getMessage(), e);
                            record.setResult("TOS服务端返回错误:状态码=" + e.getStatusCode() + ", 错误码=" + e.getCode() + ", 消息=" + e.getMessage());
                            return record;
                        } catch (Throwable t) {
                            log.error("未知异常发生", t);
                            record.setResult("未知异常发生:" + t.getMessage());
                            return record;
                        }

其中的REGION,ENDPOINT,ACCESS_KEY,SECRET_KEY,BUCKET_NAME是全局变量,根据自身情况自行设置


用键盘敲击出的不只是字符,更是一段段生活的剪影、一个个心底的梦想。希望我的文字能像一束光,在您阅读的瞬间,照亮某个角落,带来一丝温暖与共鸣。

楠笙

enfj 主人公

站长

具有版权性

请您在转载、复制时注明本文 作者、链接及内容来源信息。 若涉及转载第三方内容,还需一同注明。

具有时效性

欢迎来到楠笙的站点,为您导航全站动态

38 文章数
7 分类数
71 评论数
10标签数

热门文章