当前位置: 首页 > ds >正文

Java实现文件自动下载,XXL-Job定时任务中的HTTP文件下载最佳实践

目录

    • 1、前言
    • 2、业务背景
    • 3、核心实现
      • 1. XXL-Job任务入口
      • 2. URL生成与中文编码处理
      • 3. HTTP下载核心实现
    • 4、技术要点分析
      • 1. 中文URL编码处理
      • 2. 重试机制设计
      • 3. HTTP连接优化
      • 4. 异常处理与日志记录
    • 5、常见问题与解决方案
      • 1. 文件下载为空
      • 2. 连接超时
      • 3. 中文文件名乱码
    • 6、总结

## 更多学习内容:https://www.processon.com/mindmap/60504b5ff346fb348a93b4fa

1、前言

在企业级应用开发中,定时任务是一个常见的需求场景。特别是在气象、农业等数据密集型行业,需要定期从外部系统下载最新的数据文件。本文将基于一个真实的项目案例,详细介绍如何在XXL-Job框架中实现可靠的HTTP文件下载功能。

2、业务背景

在农业气象系统中,我们需要定期下载中长期天气预报文件,这些文件通常存储在内网文件服务器上,文件名包含中文字符和日期信息。系统需要具备以下特性:

  • 自动重试机制:如果当天文件不存在,自动尝试前几天的文件
  • 中文文件名支持:正确处理URL中的中文字符编码
  • 详细的日志记录:便于问题排查和监控
  • 异常处理:优雅处理各种网络异常情况

3、核心实现

1. XXL-Job任务入口

@XxlJob("downloadMediumAndLongTermForecast")
public ReturnT<String> downloadMediumAndLongTermForecast(String param) throws Exception {XxlJobLogger.log("获取最新的周预报文件");// 示例模板 URL(注意:文件名中有中文)String templateUrl = "http://172.18.152.109:10003/Files/Weather/SWP/Original/zqtqyb/{yyyy}/{MM}/{dd}/{yyyyMMdd}中期天气预报.docx";LocalDate currentDate = LocalDate.now();boolean success = false;final int MAX_RETRY_DAYS = 3;for (int i = 0; i <= MAX_RETRY_DAYS; i++) {String fullUrl = generateFullUrl(templateUrl, currentDate);try {XxlJobLogger.log("尝试下载:" + fullUrl);// 构建目标文件路径File targetDir = new File(dirInfo.getRoot() + "Product/");if (!targetDir.exists()) {targetDir.mkdirs();}File targetFile = new File(targetDir, "WeatherForecastNew.doc");// 下载并记录结果boolean result = downloadUsingHttpWithDebug(fullUrl, targetFile);if (result) {XxlJobLogger.log("✅ 成功下载文件:" + targetFile.getAbsolutePath());success = true;break;} else {XxlJobLogger.log("❌ 下载失败,尝试前一日...");currentDate = currentDate.minusDays(1);}} catch (Exception e) {XxlJobLogger.log("❌ 下载失败(" + e.getClass().getSimpleName() + ": " + e.getMessage() + "),尝试前一日...");currentDate = currentDate.minusDays(1);}}if (!success) {XxlJobLogger.log("❌ 在过去 " + MAX_RETRY_DAYS + " 天内均未找到有效的文件");return ReturnT.FAIL;}XxlJobLogger.log("获取最新的周预报文件完成");return ReturnT.SUCCESS;
}

2. URL生成与中文编码处理

处理中文文件名是这个实现的关键难点之一:

public static String generateFullUrl(String templateUrl, LocalDate date) {DateTimeFormatter pathFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");DateTimeFormatter filePrefixFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");String formattedDatePath = date.format(pathFormatter);String formattedDatePrefix = date.format(filePrefixFormatter);String url = templateUrl.replace("{yyyy}", String.valueOf(date.getYear())).replace("{MM}", String.format("%02d", date.getMonthValue())).replace("{dd}", String.format("%02d", date.getDayOfMonth())).replace("{yyyyMMdd}", formattedDatePrefix);// 对中文文件名进行URL编码try {// 只对文件名部分进行编码,保持路径部分不变int lastSlashIndex = url.lastIndexOf('/');if (lastSlashIndex != -1) {String pathPart = url.substring(0, lastSlashIndex + 1);String fileNamePart = url.substring(lastSlashIndex + 1);String encodedFileName = URLEncoder.encode(fileNamePart, "UTF-8").replace("+", "%20"); // 空格用%20而不是+url = pathPart + encodedFileName;}} catch (UnsupportedEncodingException e) {XxlJobLogger.log("URL编码失败: " + e.getMessage());}return url;
}

3. HTTP下载核心实现

private boolean downloadUsingHttpWithDebug(String fileUrl, File outputFile) {try {// 添加URL编码日志XxlJobLogger.log("原始URL: " + fileUrl);URL url = new URL(fileUrl);HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();// 设置更完整的请求头httpConn.setRequestMethod("GET");httpConn.setConnectTimeout(30000); // 增加到30秒httpConn.setReadTimeout(60000);    // 增加到60秒httpConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");httpConn.setRequestProperty("Accept", "*/*");httpConn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");httpConn.setRequestProperty("Accept-Encoding", "identity"); // 不使用压缩httpConn.setRequestProperty("Connection", "keep-alive");// 处理重定向httpConn.setInstanceFollowRedirects(true);int responseCode = httpConn.getResponseCode();// 输出详细调试信息XxlJobLogger.log("HTTP 状态码:" + responseCode);XxlJobLogger.log("Content-Type: " + httpConn.getContentType());XxlJobLogger.log("Content-Length: " + httpConn.getContentLength());if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP|| responseCode == HttpURLConnection.HTTP_MOVED_PERM|| responseCode == HttpURLConnection.HTTP_SEE_OTHER) {String newUrl = httpConn.getHeaderField("Location");XxlJobLogger.log("检测到重定向,新地址:" + newUrl);return downloadUsingHttpWithDebug(newUrl, outputFile); // 递归处理}if (responseCode != HttpURLConnection.HTTP_OK) {XxlJobLogger.log("服务器返回非 200 状态码: " + responseCode);// 尝试读取错误响应try (InputStream errorStream = httpConn.getErrorStream()) {if (errorStream != null) {byte[] errorBytes = new byte[1024];int bytesRead = errorStream.read(errorBytes);if (bytesRead > 0) {String errorResponse = new String(errorBytes, 0, bytesRead, "UTF-8");XxlJobLogger.log("错误响应: " + errorResponse);}}}return false;}// 开始下载try (InputStream inputStream = httpConn.getInputStream();FileOutputStream outputStream = new FileOutputStream(outputFile)) {byte[] buffer = new byte[4096];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}// 强制刷新确保写入完成outputStream.flush();// 检查文件大小是否为 0if (outputFile.length() == 0) {XxlJobLogger.log("❌ 下载文件为空(0字节)");return false;}return true;}} catch (Exception e) {XxlJobLogger.log("⚠️ 下载过程中发生异常:" + e.getClass().getName() + ": " + e.getMessage());e.printStackTrace(); // 打印完整堆栈跟踪return false;}
}

4、技术要点分析

1. 中文URL编码处理

问题:直接使用包含中文的URL会导致HTTP请求失败。

解决方案

  • 只对文件名部分进行URL编码,保持路径部分不变
  • 使用UTF-8编码
  • +替换为%20(空格的正确编码)

2. 重试机制设计

策略

  • 从当前日期开始,逐日向前尝试
  • 最多重试3天
  • 每次失败后记录详细日志

3. HTTP连接优化

关键配置

  • 连接超时:30秒
  • 读取超时:60秒
  • User-Agent:模拟浏览器请求
  • Accept-Encoding:设置为identity避免压缩问题

4. 异常处理与日志记录

最佳实践

  • 详细记录每个步骤的执行情况
  • 区分不同类型的异常
  • 提供足够的调试信息

5、常见问题与解决方案

1. 文件下载为空

原因:服务器返回200状态码但内容为空

解决:检查文件大小,如果为0字节则认为下载失败

2. 连接超时

原因:网络不稳定或服务器响应慢

解决:适当增加超时时间,添加重试机制

3. 中文文件名乱码

原因:URL编码问题

解决:正确使用URLEncoder进行UTF-8编码

6、总结

本文介绍了一个完整的HTTP文件下载解决方案,特别适用于定时任务场景。通过合理的重试机制、正确的中文编码处理和详细的日志记录,可以构建一个稳定可靠的文件下载系统。

在实际应用中,还需要根据具体的业务需求进行调整和优化。希望这个案例能为大家在类似场景下的开发工作提供参考。

http://www.xdnf.cn/news/15568.html

相关文章:

  • lightgbm算法学习
  • Datawhale AI夏令营大模型 task2.1
  • ISO-IEC-IEEE 42010架构规范
  • 更改elementui 图标 css content
  • 详解从零开始实现循环神经网络(RNN)
  • 深浅拷贝以及函数缓存
  • Dubbo高阶难题:异步转同步调用链上全局透传参数的丢失问题
  • iOS App 安全加固全流程:静态 + 动态混淆对抗逆向攻击实录
  • iOS如何查看电池容量?理解系统限制与开发者级能耗调试方法
  • 内网环境自签名超长期HTTPS证书,并在Chrome中显示为安全证书
  • C#自定义控件
  • 【Python】基础语法
  • 单向链表、双向链表、栈、队列复习(7.14)
  • LSV负载均衡
  • Usage of standard library is restricted (arkts-limited-stdlib) <ArkTSCheck>
  • 防火墙技术概述
  • Java行为型模式---模板方法模式
  • 【html基本界面】
  • 【视频格式转换】.264格式转为mp4格式
  • 7.15 窗口函数 | 二分 | 位运算
  • 互斥锁与同步锁
  • SAP-ABAP:SAP库存管理核心增强:IF_EX_MB_DOCUMENT_BADI 深度解析
  • AI驱动编程范式革命:传统开发与智能开发的全维度对比分析
  • 【人工智能】通过 Dify 构建聊天助手
  • 【t检验】用奶茶店排队案例解释
  • LangChain 和 Dify 是什么
  • 基于51单片机的贪吃蛇游戏Protues仿真设计
  • 数据分类分级和重要数据标准解读
  • 文献查找任务及其方法
  • 当前(2024-07-14)视频插帧(VFI)方向的 SOTA 基本被三篇顶会工作占据,按“精度-速度-感知质量”三条线总结如下,供你快速定位最新范式