java.io.IOException: Server returned HTTP response code: 403 for URL: http://start.spring.io

本文分析了遇到HTTP 403 Forbidden错误的原因,该错误发生在尝试通过http://start.spring.io创建Spring Boot项目时。问题源于网站启用了百度云加速的CDN和安全服务中的'CC强力防护'功能。解决方案包括通过网页创建项目并下载,然后在本地使用Maven文件覆盖项目,执行Maven Update来解决问题。同时提醒用户在选择依赖时可能会遇到其他错误,需要做好应对准备。

403 Forbidden 是HTTP协议中的一个状态码(Status Code)。可以简单的理解为没有权限访问此站。

主要原因分析

1.你的IP被列入黑名单
2.你在一定时间内过多地访问此网站(一般是用采集程序),被防火墙拒绝访问了
3.网站域名解析到了空间,但空间未绑定此域名
4.你的网页脚本文件在当前目录下没有执行权限
5.在不允许写/创建文件的目录中执行了创建/写文件操作
6.以http方式访问需要ssl连接的网址
7.浏览器不支持SSL 128时访问SSL 128的连接
8.在身份验证的过程中输入了错误的密码
9.DNS解析错误,手动更改DNS服务器地址
10.连接的用户过多,可以过后再试
11.服务器繁忙,同一IP地址发送请求过多,遭到服务器智能屏蔽

12.CC强力防护


而我遇到的属于第12种,产生问题的过程和原因往下看↓

产生问题的过程

eclipse 创建 spring boot 项目 遇到错误:java.io.IOException: Server returned HTTP response code: 403 for URL: http://start.spring.io

如下图:


// 定义该类所在的包路径,遵循 Java 的命名规范(通常是倒置的域名) package com.shop.jieyou.service; // 导入 Jackson 库的核心类,用于将 JSON 字符串解析为 Java 对象 import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; // Spring 框架注解:标识此类为一个服务组件,由 Spring 容器管理生命周期 import com.shop.jieyou.entity.UserItem; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; // Java 标准库导入:用于处理输入输出流、读取外部进程输出 import java.io.*; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Python服务类,用于执行Python爬虫脚本并获取花卉数据 * 每次调用都会直接执行Python脚本,不进行任何结果缓存 * * 功能说明: * - 调用位于 resources/scripts/crawler.py 的 Python 爬虫脚本 * - 获取其标准输出(应为 JSON 格式) * - 将 JSON 解析为 List<Map<String, Object>> 结构返回给控制器 * - 不使用缓存机制,每次请求均重新运行脚本 */ @Service // 表示这是一个 Spring Service Bean,可被自动扫描并注入到其他组件中 public class PythonService { // 使用 Jackson 提供的 ObjectMapper 实例来序列化/反序列化 JSON 数据 private final ObjectMapper objectMapper = new ObjectMapper(); // 定义 Python 脚本在项目中的相对路径 // 注意:此路径是相对于项目根目录的,适用于开发环境 // 生产环境中可能需要改为绝对路径或通过配置文件指定 private static final String SCRIPT_PATH = "src/main/resources/scripts/crawler.py"; /** * 执行 Python 爬虫脚本以获取花卉信息列表 * * 此方法会: * 1. 启动一个新的操作系统进程来运行 Python 脚本 * 2. 捕获脚本的标准输出 * 3. 验证执行状态(退出码) * 4. 解析输出为 Java 对象 * 5. 返回结构化数据 * * @return 包含花卉信息的 Map 列表,每个 Map 表示一种花卉的字段(如 name, family 等) * @throws IOException 当发生 I/O 错误(如无法启动进程、读取输出失败)时抛出 * @throws InterruptedException 当当前线程在等待进程结束时被中断,通常发生在 JVM 关闭期间 */ public synchronized List<Map<String, Object>> getFlowers() throws IOException, InterruptedException { // 创建一个 ProcessBuilder 实例,用于构建和启动外部进程 // 参数:"python" 是命令,SCRIPT_PATH 是要执行的脚本路径 ProcessBuilder pb = new ProcessBuilder("python", SCRIPT_PATH); // 设置合并错误流到标准输出流 // 这样可以通过同一个 BufferedReader 同时读取正常输出和错误信息 // 便于调试问题(例如 Python 报错 ImportError) pb.redirectErrorStream(true); // 启动外部进程(即运行 python crawler.py) // process 对象可用于控制该进程(等待、杀死等) Process process = pb.start(); // 使用 try-with-resources 确保 BufferedReader 在使用后自动关闭 // InputStreamReader 将字节流转换为字符流,并指定 UTF-8 编码以正确处理中文 try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { // 用于拼接从 Python 脚本输出的所有文本行 StringBuilder output = new StringBuilder(); String line; // 循环读取每行输出,直到流结束(EOF) while ((line = reader.readLine()) != null) { // 去除每行首尾空白(包括换行符、空格),然后追加到总输出中 // 注意:这里没有添加换行符,意味着所有内容会被压缩成一行 output.append(line.trim()); } // 等待 Python 进程执行完成,并获取其退出码 // 正常情况下应返回 0;非零值表示异常退出(如语法错误、模块未安装) int exitCode = process.waitFor(); // 如果退出码不是 0,说明脚本执行失败 if (exitCode != 0) { throw new RuntimeException("Python script exited with code: " + exitCode); } // 检查输出是否为空 // 即使脚本成功退出,也可能未打印任何有效数据 if (output.length() == 0) { throw new RuntimeException("Python script returned empty output"); } // 使用 Jackson 反序列化 JSON 字符串为 Java 对象 // TypeReference 是泛型辅助类,告诉 ObjectMapper 我们想要的是 List<Map<String, Object>> // 每个 Map 对应一条花卉记录,key 是字段名(如 "name"),value 是对应值 List<Map<String, Object>> result = objectMapper.readValue(output.toString(), new TypeReference<List<Map<String, Object>>>() {}); // 检查返回的数据中是否包含错误信息(假设 Python 脚本约定第一个元素带 error 字段表示失败) if (!result.isEmpty() && result.get(0).containsKey("error")) { throw new RuntimeException("爬虫错误: " + result.get(0).get("error")); } // 成功解析并验证后,返回花卉数据列表 return result; } catch (Exception e) { // 异常处理:确保即使出错也能清理系统资源 // 如果进程仍在运行,则强制终止它,防止僵尸进程或资源泄漏 if (process.isAlive()) { process.destroyForcibly(); } // 继续向上抛出异常,让调用者知道发生了什么 throw e; } } @Autowired JdbcTemplate jdbcTemplate; public List<UserItem> getUserItemMatrix() { String sql = "SELECT user_id, product_id, COUNT(*) as count " + "FROM tb_order WHERE state = 1 " + "GROUP BY user_id, product_id"; return jdbcTemplate.query(sql, (rs, rowNum) -> new UserItem( rs.getLong("user_id"), rs.getLong("product_id"), rs.getInt("count") ) ); } private static final String PYTHON_SCRIPT_PATH = "src/main/resources/scripts/python-model/infer.py"; private static final String PYTHON_ENV_PATH = "python"; // 虚拟环境 public Map<String, Object> classify(String file) throws IOException, InterruptedException, Exception { // 1. 校验文件类型(安全) // if (!"image/jpeg".equals(file) && !"image/png".equals(file)) { // throw new IllegalArgumentException("仅支持 JPG/PNG 图片"); // } // 2. 保存临时文件 File tempFile = File.createTempFile("upload_", ".jpg"); try (FileOutputStream fos = new FileOutputStream(tempFile)) { fos.write(file.getBytes()); } // 3. 调用 Python 脚本 ProcessBuilder pb = new ProcessBuilder( PYTHON_ENV_PATH, PYTHON_SCRIPT_PATH, tempFile.getAbsolutePath() ); pb.redirectErrorStream(true); Process process = pb.start(); // 4. 获取输出结果 StringBuilder output = new StringBuilder(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { output.append(line); } } int exitCode = process.waitFor(); if (exitCode != 0) { throw new RuntimeException("Python 脚本执行失败,退出码: " + exitCode); } // 5. 解析 JSON 结果(假设 Python 返回的是 JSON 字符串) ObjectMapper mapper = new ObjectMapper(); JsonNode jsonNode = mapper.readTree(output.toString().trim()); Map<String, Object> result = new HashMap<>(); result.put("predicted_class", jsonNode.get("predicted_class").asText()); result.put("confidence", jsonNode.get("confidence").asDouble()); return result; } } # python-model/infer.py import numpy as np import sys import os import json import logging from PIL import Image os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" logging.getLogger("tensorflow").setLevel(logging.FATAL) import tensorflow as tf MODEL_PATH = "animal_classifier.h5" IMG_SIZE = 224 CLASS_NAMES = ['cat', 'dog', 'elephant', 'fish'] # 必须与训练时一致! def predict(image_path): if not os.path.exists(image_path): print(json.dumps({"error": f"图片不存在: {image_path}"})) return try: # 加载模型 if not os.path.exists(MODEL_PATH): print(json.dumps({"error": f"模型未找到: {MODEL_PATH}"})) return model = tf.keras.models.load_model(MODEL_PATH) # 加载并预处理图像 img = Image.open(image_path).convert("RGB") img = img.resize((IMG_SIZE, IMG_SIZE)) img_array = np.array(img) / 255.0 img_array = np.expand_dims(img_array, axis=0) # 添加 batch 维度 # 预测 preds = model.predict(img_array, verbose=0) confidence = float(np.max(preds)) label_idx = np.argmax(preds) label = CLASS_NAMES[label_idx] result = { "success": True, "predicted_class": label, "confidence": round(confidence, 4), "all_probabilities": { CLASS_NAMES[i]: round(float(preds[0][i]), 4) for i in range(len(CLASS_NAMES)) } } print(json.dumps(result)) except Exception as e: print(json.dumps({"error": f"预测失败: {str(e)}"})) if __name__ == "__main__": if len(sys.argv) != 2: print(json.dumps({"error": "用法: python infer.py <image_path>"})) else:package com.shop.jieyou.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.shop.jieyou.common.Result; import com.shop.jieyou.entity.UserItem; import com.shop.jieyou.service.ItemService; import com.shop.jieyou.service.PythonService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; /** * 花卉相关接口控制器 * 提供三大功能: * 1. 获取中国十大名花数据(来自爬虫或缓存) * 2. 手动刷新花卉数据(强制重新爬取) * 3. 基于用户行为的花卉推荐(调用Python协同过滤脚本) */ @RestController @CrossOrigin(origins = "*") // 允许所有域访问,用于前端开发调试(生产环境建议限制域名) @RequestMapping("/api") public class FlowerController { @Autowired private PythonService pythonService; // 注入业务服务层,处理数据获取与推荐逻辑 @Autowired private ItemService itemService; /** * 接口:GET /api/flowers * 功能:获取“中国十大名花”数据列表 * 数据来源:可能来自数据库、Redis 缓存 或 调用 Python 爬虫脚本 * * @return Result<List<Map<String, Object>>> 返回包含花卉信息的成功响应 */ @GetMapping("/flowers") public Result<List<Map<String, Object>>> getTopTenFlowers() { try { // 调用服务层获取花卉数据(内部可能带缓存机制) List<Map<String, Object>> flowers = pythonService.getFlowers(); return Result.success(flowers); // 成功返回数据 } catch (Exception e) { // 捕获异常并统一返回错误码和消息,避免暴露堆栈给前端 return Result.error("500", "获取花卉数据失败:" + e.getMessage()); } } /** * 接口:POST /api/flowers/refresh * 功能:强制刷新花卉数据缓存,触发重新爬取 * 使用场景:管理员手动更新数据时调用 * * @return Result<Map<String, Object>> 返回刷新结果信息 */ @PostMapping("/flowers/refresh") public Result<Map<String, Object>> refreshData() { try { // TODO: 如果实现了 clearCache 方法,请取消注释并调用 // pythonService.clearCache(); // 清除旧缓存,下次 getFlowers 将重新爬取 // 重新获取最新数据(假设此时会触发爬虫) List<Map<String, Object>> flowers = pythonService.getFlowers(); // 构造返回信息 Map<String, Object> data = new HashMap<>(); data.put("message", "数据已刷新"); data.put("count", flowers.size()); return Result.success(data); } catch (Exception e) { return Result.error("500", "刷新失败:" + e.getMessage()); } } // ========== 推荐系统相关常量定义 ========== /** * 输入文件路径:Java 将用户-商品行为数据写入此 JSON 文件供 Python 脚本读取 * 注意:src/main/resources 是编译后打包进 jar 的资源目录,不适合运行时写入! * 建议改为外部路径如 "./data/input.json" */ private static final String INPUT_PATH = "src/main/resources/scripts/input.json"; /** * 输出文件路径:Python 脚本将推荐结果写入此文件,Java 再读取返回给前端 */ private static final String OUTPUT_PATH = "src/main/resources/scripts/output.json"; /** * Python 协同过滤脚本路径 * 注意:resources 目录下的 .py 文件在打包后无法直接作为可执行脚本运行 * 更佳做法是将脚本放在项目外部或使用 ProcessBuilder 启动独立服务 */ private static final String PYTHON_SCRIPT = "src/main/resources/scripts/collaborative.py"; /** * 接口:GET /api/recommend?userId=123 * 功能:为指定用户生成个性化花卉推荐列表 * 实现方式:Java 查询数据库 → 写入 JSON 文件 → 调用 Python 脚本计算 → 读取结果返回 * * @param userId 用户ID,必填参数 * @return Result<JsonNode> 推荐的商品ID数组(如 [101, 105, 108]) */ @GetMapping("/recommend") public Result recommendFlowers(@RequestParam("userId") Long userId) { try { // 1. 获取用户行为数据 List<UserItem> matrix = pythonService.getUserItemMatrix(); // 2. 调用 Python 脚本(通过 stdin/stdout 通信) ProcessBuilder pb = new ProcessBuilder("python", PYTHON_SCRIPT, String.valueOf(userId)); pb.redirectErrorStream(true); // 合并错误流 Process process = pb.start(); // 3. 将数据写入脚本的标准输入 ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(process.getOutputStream(), matrix); process.getOutputStream().close(); // 关闭输入,通知Python结束读取 // 4. 读取Python脚本的输出(推荐结果) JsonNode result = mapper.readTree(process.getInputStream()); // 5. 等待脚本执行完毕 int exitCode = process.waitFor(); if (exitCode != 0) { return Result.error("500", "Python script failed with exit code: " + exitCode); } System.out.println(result); return Result.success(result); } catch (Exception e) { e.printStackTrace(); return Result.error("500", "推荐生成失败:" + e.getMessage()); } } @PostMapping("/predict") public ResponseEntity<String> predict(@RequestParam("file") MultipartFile file) { try { // 保存上传的文件到临时路径 String tempDir = System.getProperty("java.io.tmpdir"); File tempFile = new File(tempDir, file.getOriginalFilename()); file.transferTo(tempFile); // 调用 Python 脚本执行预测 ProcessBuilder pb = new ProcessBuilder( "D:\\Python\\python.exe", "D:/DevCode/商城/Shop-master/shop-springboot/src/main/resources/scripts/image_classifier.py", "predict", tempFile.getAbsolutePath() ); pb.redirectErrorStream(true); // 合并 stdout 和 stderr Process process = pb.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); StringBuilder output = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { output.append(line); } int exitCode = process.waitFor(); if (exitCode == 0) { return ResponseEntity.ok(output.toString().trim()); } else { return ResponseEntity.status(500).body("{\"error\": \"Prediction failed\"}"); } } catch (Exception e) { return ResponseEntity.status(500).body("{\"error\": \"" + e.getMessage() + "\"}"); } } private static final String PYTHON_EXECUTABLE = "D:\\Python\\python.exe"; // 或 "python3" private static final String INFER_SCRIPT_PATH = "D:/DevCode/商城/Shop-master/shop-springboot/src/main/resources/scripts/python-model/infer.py"; @PostMapping("/uploadPython") public Result<Map<String, Object>> classifyImage(@RequestParam String file) { if (file.isEmpty()) { return Result.error("400", "文件为空"); } try { // 只做参数传递,逻辑交给 Service Map<String, Object> result = pythonService.classify(file); return Result.success(result); } catch (IOException e) { return Result.error("500", "文件处理失败:" + e.getMessage()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return Result.error("500", "处理被中断"); } catch (Exception e) { return Result.error("500", "识别出错:" + e.getMessage()); } } } predict(sys.argv[1])
最新发布
10-22
Java程序中,`java.io.IOException: Server returned HTTP response code: 400 for URL:` 错误通常表示客户端向服务器发送的请求存在问题,服务器无法理解该请求。以下是一些可能的解决方法: ### 检查请求参数 确保请求中传递的参数正确无误,包括参数的名称、类型和值。参数缺失、格式错误或值不符合服务器要求都可能导致 400 错误。 ```java import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; public class HttpRequestExample { public static void main(String[] args) { try { // 构建请求URL String baseUrl = "http://example.com/api"; String param1 = "value1"; String param2 = "value2"; String encodedParam1 = URLEncoder.encode(param1, StandardCharsets.UTF_8); String encodedParam2 = URLEncoder.encode(param2, StandardCharsets.UTF_8); String fullUrl = baseUrl + "?param1=" + encodedParam1 + "&param2=" + encodedParam2; // 打开连接 URL url = new URL(fullUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); // 获取响应 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; StringBuilder response = new StringBuilder(); while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } } } ``` ### 检查请求头 某些服务器可能要求特定的请求头信息,如 `Content-Type`、`User-Agent` 等。确保请求头设置正确。 ```java import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class HttpRequestWithHeadersExample { public static void main(String[] args) { try { // 构建请求URL URL url = new URL("http://example.com/api"); // 打开连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("User-Agent", "Mozilla/5.0"); // 获取响应 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; StringBuilder response = new StringBuilder(); while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } } } ``` ### 检查请求体 如果是 POST 请求,确保请求体的格式和内容正确。例如,如果服务器期望的是 JSON 格式的请求体,要确保 JSON 格式正确。 ```java import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class PostRequestWithBodyExample { public static void main(String[] args) { try { // 构建请求URL URL url = new URL("http://example.com/api"); // 打开连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json"); // 设置请求体 String requestBody = "{\"key\": \"value\"}"; connection.setDoOutput(true); DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); outputStream.writeBytes(requestBody); outputStream.flush(); outputStream.close(); // 获取响应 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; StringBuilder response = new StringBuilder(); while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } } } ``` ### 调试和日志记录 在代码中添加调试信息,打印请求的 URL、请求头、请求体等信息,有助于定位问题。 ```java import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class DebuggingExample { public static void main(String[] args) { try { // 构建请求URL URL url = new URL("http://example.com/api"); // 打开连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json"); // 设置请求体 String requestBody = "{\"key\": \"value\"}"; connection.setDoOutput(true); DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); outputStream.writeBytes(requestBody); outputStream.flush(); outputStream.close(); // 打印请求信息 System.out.println("Request URL: " + url); System.out.println("Request Method: " + connection.getRequestMethod()); System.out.println("Request Headers: " + connection.getRequestProperties()); System.out.println("Request Body: " + requestBody); // 获取响应 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; StringBuilder response = new StringBuilder(); while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); System.out.println("Response: " + response.toString()); } catch (Exception e) { e.printStackTrace(); } } } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值