| | |
| | | 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; |
| | |
| | | return objectId; |
| | | } |
| | | try { |
| | | HttpURLConnection conn = (HttpURLConnection) new URL(input).openConnection(); |
| | | conn.setInstanceFollowRedirects(true); |
| | | // 手动跟随重定向: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); |
| | | conn.connect(); |
| | | String finalUrl = conn.getURL().toString(); |
| | | conn.disconnect(); |
| | | return extractObjectId(finalUrl); |
| | | 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; |