rk
5 小时以前 cf1d82548a1bd8155ffe9b486df8167aa9e63a7d
server/services/src/main/java/com/doumee/core/douyin/DouyinClient.java
@@ -27,6 +27,7 @@
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
@@ -277,15 +278,50 @@
            return objectId;
        }
        try {
            HttpURLConnection conn = (HttpURLConnection) new URL(input).openConnection();
            conn.setInstanceFollowRedirects(true);
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.connect();
            String finalUrl = conn.getURL().toString();
            conn.disconnect();
            return extractObjectId(finalUrl);
            // 手动跟随重定向:HttpURLConnection 自动重定向不跨协议(http↔https)且 getURL() 更新不稳定,
            // 改为逐跳读取 Location 头,最终从落地长链里提取 object_id
            String current = input;
            int maxRedirects = 5;
            for (int i = 0; i < maxRedirects; i++) {
                HttpURLConnection conn = (HttpURLConnection) new URL(current).openConnection();
                conn.setInstanceFollowRedirects(false); // 手动跟随
                conn.setRequestMethod("GET");
                conn.setRequestProperty("User-Agent", "Mozilla/5.0");
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                int code = conn.getResponseCode();
                String location = conn.getHeaderField("Location");
                // 必须读取并关闭错误/输入流,否则连接资源泄漏
                try (InputStream is = (code >= 400) ? conn.getErrorStream() : conn.getInputStream()) {
                    if (is != null) {
                        // 不需要内容,仅消费流以释放连接
                        byte[] buf = new byte[1024];
                        while (is.read(buf) > 0) {
                            // drain
                        }
                    }
                }
                if (code == HttpURLConnection.HTTP_MOVED_PERM
                        || code == HttpURLConnection.HTTP_MOVED_TEMP
                        || code == HttpURLConnection.HTTP_SEE_OTHER
                        || code == 307 || code == 308) {
                    if (StringUtils.isBlank(location)) {
                        // 重定向但无 Location,无法继续
                        return null;
                    }
                    current = location;
                    continue;
                }
                // 非重定向:用最终 URL 提取 object_id
                String resolved = location != null ? location : conn.getURL().toString();
                objectId = extractObjectId(resolved);
                if (objectId == null) {
                    log.warn("解析抖音短链未提取到 object_id,input={},final={}", input, resolved);
                }
                return objectId;
            }
            log.warn("解析抖音短链超过最大重定向次数,input={}", input);
            return null;
        } catch (Exception e) {
            log.error("解析抖音短链异常:{}", input, e);
            return null;