Java HttpServer保存APK文件为空问题排查求助
问题原因与修复方案
你的代码核心问题是请求体的输入流被重复读取,导致第二次读取时已经没有内容:
- 当调用
up.parseRequest(...)时,ServletFileUpload已经将he.getRequestBody()的输入流完全读取并解析成FileItem列表,此时输入流指针已走到末尾。 - 之后再调用
InputStream inputStream = he.getRequestBody()拿到的流是空的,inputStream.read(buffer)直接返回-1,没有字节写入文件,最终文件大小为0。
修复步骤
1. 直接用FileItem的流保存文件
不需要重新获取请求体流,直接使用每个FileItem提供的getInputStream()读取文件内容:
// 移除原inputStream相关代码,替换为遍历FileItem处理 for (FileItem fi : result) { if (!fi.isFormField()) { // 排除普通表单字段,只处理文件项 // 构建保存路径,确保父目录存在 File apkDir = new File(Environment.getExternalStorageDirectory(), "test/configXml"); apkDir.mkdirs(); File apkFile = new File(apkDir, "test.apk"); try (InputStream fis = fi.getInputStream(); FileOutputStream fos = new FileOutputStream(apkFile)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } Log.i(HttpServerManager.getHttpServerManager().TAG, "文件保存完成,大小:" + fi.getSize()); } } // 处理响应输出 os.write(fi.getName().getBytes()); os.write("\r\n".getBytes()); Log.i(HttpServerManager.getHttpServerManager().TAG, "File-Item: " + fi.getFieldName() + " = " + fi.getName() + " get size " + fi.getSize() ); }
2. 修正getContentLength实现
RequestContext的getContentLength()返回0是错误的,需返回实际请求内容长度,否则ServletFileUpload可能无法正确解析:
@Override public int getContentLength() { String length = he.getRequestHeaders().getFirst("Content-Length"); return length != null ? Integer.parseInt(length) : -1; }
3. 调整响应发送时机
建议在文件处理完成后再发送响应,避免提前发送导致的潜在问题:
// 先处理文件,再发送响应 he.getResponseHeaders().add("Content-type", "text/plain"); String responseText = "文件上传完成"; he.sendResponseHeaders(200, responseText.getBytes().length); try (OutputStream os = he.getResponseBody()) { os.write(responseText.getBytes()); }
4. 优化路径拼接
不要硬拼路径,用File类的构造方法保证跨平台兼容性:
// 替代硬拼的路径写法 File apkDir = new File(Environment.getExternalStorageDirectory(), "test/configXml"); apkDir.mkdirs(); File apkFile = new File(apkDir, "test.apk");
完整修复后的核心代码片段
class postApkFile implements HttpHandler { @RequiresApi(api = Build.VERSION_CODES.O) @Override public void handle(HttpExchange he) throws IOException { he.getResponseHeaders().add("Access-Control-Allow-Origin", "*"); if (he.getRequestMethod().equalsIgnoreCase("OPTIONS")) { he.getResponseHeaders().add("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); // 补充POST方法 he.getResponseHeaders().add("Access-Control-Allow-Headers", "Content-Type,Authorization"); he.sendResponseHeaders(204, -1); return; } DiskFileItemFactory d = new DiskFileItemFactory(); try { ServletFileUpload up = new ServletFileUpload(d); List<FileItem> result = up.parseRequest(new RequestContext() { @Override public String getCharacterEncoding() { return "UTF-8"; } @Override public int getContentLength() { String length = he.getRequestHeaders().getFirst("Content-Length"); return length != null ? Integer.parseInt(length) : -1; } @Override public String getContentType() { return he.getRequestHeaders().getFirst("Content-type"); } @Override public InputStream getInputStream() throws IOException { return he.getRequestBody(); } }); StringBuilder responseSb = new StringBuilder(); // 处理文件保存 for (FileItem fi : result) { responseSb.append(fi.getName()).append("\r\n"); Log.i(HttpServerManager.getHttpServerManager().TAG, "File-Item: " + fi.getFieldName() + " = " + fi.getName() + " get size " + fi.getSize() ); if (!fi.isFormField()) { File apkDir = new File(Environment.getExternalStorageDirectory(), "test/configXml"); if (!apkDir.exists()) { apkDir.mkdirs(); } File apkFile = new File(apkDir, "test.apk"); try (InputStream fis = fi.getInputStream(); FileOutputStream fos = new FileOutputStream(apkFile)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } } } } // 发送响应 he.getResponseHeaders().add("Content-type", "text/plain"); byte[] responseBytes = responseSb.toString().getBytes(); he.sendResponseHeaders(200, responseBytes.length); try (OutputStream os = he.getResponseBody()) { os.write(responseBytes); } } catch (Exception e) { e.printStackTrace(); // 发送错误响应 he.sendResponseHeaders(500, -1); } } }
内容的提问来源于stack exchange,提问作者Ozgur Baykal
相关产品推荐
相关产品推荐

