4.从Request中获取文件数据

本文介绍了如何在Web开发中从HTTP请求中获取并处理文件数据,重点讨论了文件上传的相关技术,包括multipart/form-data编码、文件流处理以及错误处理策略。

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * 工具类 -从HttpServletRequest中获取文件数据
 * 
 */
public class RequestUtil {

    /**
     * 从HttpServletRequest中获取所有的文件
     * 
     * @param request
     * @return FileItem对象列表
     * @throws FileUploadException
     */
    public static List<FileItem> getFileItemList(HttpServletRequest request) throws FileUploadException {
        List<FileItem> fileItemFromRequestList = getDataFromRequest(request);
        if (fileItemFromRequestList != null && !fileItemFromRequestList.isEmpty()) {
            List<FileItem> fileItemList = new ArrayList<>();
            for (FileItem fileItem : fileItemFromRequestList) {
                if (!fileItem.isFormField()) { // 文件
                    fileItemList.add(fileItem);
                }
            }
            return fileItemList;
        }
        return null;
    }

    /**
     * 从request中获取FileItem对象列表
     * 
     * @param request
     * @return fileItemFromRequestList
     * @throws FileUploadException
     */
    private static List<FileItem> getDataFromRequest(HttpServletRequest request) throws FileUploadException {
        String tempPath = request.getServletContext().getRealPath("/temp");
        File file = new File(tempPath);
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(1024 * 1024 * 1024);// 设置缓存大小
        factory.setRepository(file);// 默认情况下 临时文件不会自动删除
        ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
        List<FileItem> fileItemFromRequestList = servletFileUpload.parseRequest(request);
        file.delete(); // 删除临时文件
        return fileItemFromRequestList;
    }
}
<think>好的,我现在需要解决用户的问题:如何在Java Servlet中使用request.getInputStream()读取并处理上传的文件内容。用户提到了他们之前尝试过使用Spring的上传组件但遇到兼容性问题,特别是手机端上传时的问题,所以需要自己处理原始流。首先,我得回忆一下Servlet中处理文件上传的基本流程。用户上传的文件通常是通过HTTPPOST请求的body部分传输的,特别是使用multipart/form-data格式。但是直接使用request.getInputStream()获取的是整个请求体的原始数据流,需要手动解析。根据引用内容,用户提到的问题之一是request.getInputStream()只能读取一次,这是因为流一旦被读取,position就到了末尾,无法重置。参考引用[3]和[4],解决方法是用Filter将ServletRequest包装,缓存输入流的数据,这样就可以多次读取。这部分需要实现一个HttpServletRequestWrapper,覆盖getInputStream和getReader方法,使其从缓存的字节数组读取数据。接下来,用户可能需要解析multipart格式的数据。根据引用[2],用户之前自己开发了读取文件流的方法,利用HTTP协议中的空行分割头和内容。这需要解析请求头中的Content-Type,找到boundary,然后根据boundary分割不同的部分。每个部分可能包含文件数据或表单字段。需要注意,不同的客户端(如Android和iOS)可能在请求头上有差异,所以需要通用的解析方法。例如,找到boundary后,逐行读取直到遇到空行,之后的内容就是文件内容,直到下一个boundary出现为止。另外,引用[2]提到使用字节流而不是字符流,因为文件(如图片)是二进制数据,使用字符流会导致损坏。所以在读取时,必须使用InputStream,并且保存为字节数组或写入文件的输出流。步骤总结: 1.创建Filter,使用HttpServletRequestWrapper缓存输入流。2.在Servlet中读取已缓存的输入流。 3.解析multipart请求体,提取文件名和文件内容。4.处理二进制数据,保存到服务器或进行其他操作。 可能遇到的问题包括正确解析multipart格式,处理不同的编码和boundary格式,以及确保数据读取的完整性。需要测试不同客户端的上传请求,确保解析方法兼容性强。 最后,生成相关问题时要围绕文件上传的深入问题、流处理的优化以及Servlet的相关机制展开。</think>### 解决方案:使用`request.getInputStream()`处理文件上传 #### 1. 输入流重复读取问题 由于`request.getInputStream()`只能读取一次[^4],需通过过滤器(Filter)对请求进行包装,缓存输入流数据。步骤如下: ```java public class CachedBodyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException { // 将输入流缓存到字节数组 byte[] body = IOUtils.toByteArray(request.getInputStream()); chain.doFilter(new CachedBodyRequestWrapper(request, body), response); } } class CachedBodyRequestWrapper extends HttpServletRequestWrapper { private final byte[] cachedBody; public CachedBodyRequestWrapper(ServletRequest request, byte[] body) { super((HttpServletRequest) request); this.cachedBody = body; } @Override public ServletInputStream getInputStream() { return new CachedBodyServletInputStream(cachedBody); } } class CachedBodyServletInputStream extends ServletInputStream { private final ByteArrayInputStream inputStream; public CachedBodyServletInputStream(byte[] body) { this.inputStream = new ByteArrayInputStream(body); } @Override public boolean isFinished() { /* 实现状态判断 */ } @Override public int read() { return inputStream.read(); } } ``` #### 2. 解析multipart/form-data格式 通过解析`Content-Type`头获取`boundary`标识符,分割文件内容: ```java String contentType = request.getContentType(); String boundary = "--" + contentType.split("boundary=")[1].trim(); // 示例:boundary=----WebKitFormBoundaryXYZ ``` #### 3. 提取文件内容 逐行读取输入流,识别文件字段: ```java try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { if (line.contains("Content-Disposition")) { // 解析文件名:name="file"; filename="example.jpg" String fileName = extractFileName(line); // 跳过头部直到空行 while (!line.isEmpty()) line = reader.readLine(); // 读取文件内容直到下一个boundary ByteArrayOutputStream fileBytes = new ByteArrayOutputStream(); while ((line = reader.readLine()) != null && !line.startsWith(boundary)) { fileBytes.write(line.getBytes(StandardCharsets.UTF_8)); } // 保存文件 Files.write(Paths.get("/upload/" + fileName), fileBytes.toByteArray()); } } } ``` #### 4. 注意事项 - **二进制流处理**:避免使用字符流(如`Reader`),直接操作字节流[^2]。 - **边界条件**:确保正确处理`boundary`的起始和结束标记(如`--boundary--`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值