使用Servlet根据浏览器request的get方法获取值,将磁盘中与之对应的json数据删除的方法

本文介绍了一个使用Java Servlet实现的学生信息删除功能。该Servlet通过GET请求接收学生姓名参数,从本地JSON文件读取并解析学生数据,然后删除指定学生记录,并更新文件。涉及的技术包括Java IO、Gson库及Servlet API。
package com.swift;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;

/**
 * Servlet implementation class DeleteServlet
 */
@WebServlet("/delete")
public class DeleteServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public DeleteServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        File file=new File("d:/student.json");
        //获取全部学生对象
        List<Student> list=getAllStudent(file);
        System.out.println(list.toString());
        //获取要删除学生姓名
        String name=request.getParameter("name");
        System.out.println(name);
        //从list对象列表中删除匹配姓名的学生对象
        deleteStudentByName(response, list, name);
        System.out.println(list.toString());
        //将删除后的对象列表写入磁盘文件
        StringBuffer sb=new StringBuffer();//将剩下的json信息保存到sb中,不然文件中只能得到最后一个json数据
        Gson gson=new Gson();
        for(Student stu:list) {
            String json=gson.toJson(stu);
            sb.append(json);
            sb.append("\r\n");
        }
        writeToFile(file, sb.toString());
    }

    private void deleteStudentByName(HttpServletResponse response, List<Student> list, String name) throws IOException {
        for(int i=0;i<list.size();i++) {
            if(list.get(i).getName().equals(name)) {
                list.remove(i);
                response.getWriter().println("You have already delete the name "+name);
            }
        }
    }

    private void writeToFile(File file,String json) {
        PrintWriter pw = null;
        try {
            try {
                pw=new PrintWriter(new OutputStreamWriter(new FileOutputStream(file),"utf-8"));
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            pw.println(json);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            pw.close();
        }
    }
    
    private List<Student> getAllStudent(File file) throws UnsupportedEncodingException, FileNotFoundException, IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(file),"utf-8"));
        List<Student> list=new ArrayList<Student>();
        String str;
        while((str=br.readLine())!=null) {
            Gson gson=new Gson();
            Student stu=gson.fromJson(str, Student.class);
            list.add(stu);
        }
        br.close();
        return list;
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

 

<think>我们正在处理一个关于Spring MultipartFile在并发情况下出现'Failed to get request parts'异常的问题。 根据用户提供的引用和问题,我们可以推断用户在使用Spring处理文件上传时,在高并发场景下遇到了取请求部分失败的问题。 引用[2]提到,Spring使用CommonsMultipartResolver(依赖于commons-fileupload)来判断是否为上传请求,并解析请求。 引用[1]则说明MultipartFile参数可以通过@RequestParam或@RequestPart访问,并且@RequestPart可以处理JSON内容。 问题现象:并发时出现'Failed to get request parts',这通常意味着在解析多部分请求时出现了问题。 可能的原因: 1. 在高并发情况下,Servlet容器(如Tomcat)对同一个请求的输入流进行多次读取,而输入流只能读取一次。 如果多个线程尝试同时解析同一个请求,可能会导致问题。 2. 使用的MultipartResolver(如CommonsMultipartResolver)不是线程安全的,或者其配置不正确。 3. 文件上传请求处理过程中,由于并发量大,资源(如临时目录、内存)不足。 解决方案: 1. 确保每个请求只被解析一次。在Spring中,MultipartResolver通常会在DispatcherServlet中解析请求一次,并将结果放在请求属性中。 但如果在其他地方(如过滤器、拦截器)提前读取了请求体,可能会导致后续解析失败。因此,避免在解析多部分请求之前读取请求体。 2. 检查MultipartResolver的配置。例如,使用CommonsMultipartResolver时,可以设置最大内存使用、最大上传大小、临时存储路径等。 确保临时存储路径存在且具有写权限,并且有足够的空间。 3. 考虑使用异步处理或增加服务器资源(如内存、线程池大小)来应对高并发。 4. 如果使用Tomcat,注意Tomcat对请求体的处理。在Tomcat中,请求体默认是阻塞读取的,如果并发上传大文件,可能会导致线程阻塞。 可以考虑使用非阻塞上传(如Servlet 3.0的异步处理)或者调整连接器(Connector)的配置,如增加maxConnections、maxThreads等。 5. 检查是否有并发修改请求的情况,比如在拦截器中修改了请求,导致后续解析失败。 6. 考虑使用分布式锁(如引用[3]提到的)来避免并发问题?但是注意,文件上传请求通常是独立的,每个请求应该被独立处理,所以这里分布式锁可能不是合适的解决方案。 但是,如果多个请求在操作同一个共享资源(如同一个文件路径),那么可能需要锁。然而,对于请求解析本身,应该不需要锁。 7. 升级Spring和Servlet容器的版本,以修复可能存在的已知问题。 8. 使用StandardServletMultipartResolver代替CommonsMultipartResolver(如果使用Servlet 3.0+)。StandardServletMultipartResolver基于Servlet 3.0的多部分解析,通常性能更好,并且可能更稳定。 具体配置示例(使用StandardServletMultipartResolver): 在Spring配置中: @Bean public MultipartResolver multipartResolver() { return new StandardServletMultipartResolver(); } 同时,在Servlet容器中配置(例如,在Spring Boot中,可以在application.properties中配置): spring.servlet.multipart.enabled=true spring.servlet.multipart.max-file-size=128KB spring.servlet.multipart.max-request-size=128KB ...(其他配置) 但是注意,StandardServletMultipartResolver需要Servlet 3.0以上的容器支持。 9. 如果使用CommonsMultipartResolver,确保配置了正确的临时目录,并且该目录不会因为并发上传而出现冲突。例如: CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setUploadTempDir(new FileSystemResource("/tmp")); multipartResolver.setMaxUploadSize(1000000); ... 10. 监控系统资源,确保没有达到系统限制(如打开文件数限制、磁盘空间等)。 11. 异常处理:在出现该异常时,可以重试或者给用户友好的提示。 由于用户提到并发异常,因此重点应放在并发处理上。建议首先检查是否在解析请求之前有对请求体的读取操作,然后考虑调整MultipartResolver的配置或更换解析器。 相关问题: 1. 如何配置Spring的MultipartResolver? 2. 如何处理大文件上传? 3. 如何优化Spring应用以支持高并发文件上传? 4. Servlet容器对文件上传的支持有哪些? 5. 如何避免在文件上传时出现资源不足? 6. 使用StandardServletMultipartResolver和CommonsMultipartResolver有什么区别? 7. 在Spring Boot中如何配置文件上传参数? 8. 如何监控文件上传过程中的资源使用情况? 9. 在分布式环境下,文件上传如何处理? 10. 如何设计一个高可用的文件上传服务?</think>### Spring MultipartFile "Failed to get request parts" 并发异常分析解决 #### 原因分析 1. **请求解析冲突** Spring 的 `MultipartResolver`(如 `CommonsMultipartResolver`)在解析请求时,会读取 Servlet 请求的输入流。**输入流只能被读取一次**。在高并发场景下,若多个线程尝试同时解析同一个请求的输入流,会导致 `Failed to get request parts` 异常[^2]。 2. **资源竞争** 文件上传涉及临时文件创建和内存缓存。当并发请求超过系统资源(如内存、临时目录 I/O)时,解析过程可能失败。例如: - 内存溢出:大量文件同时上传超出 JVM 堆内存。 - 临时目录冲突:多个请求竞争同一临时文件路径。 3. **Servlet 容器限制** Tomcat/Jetty 等容器对并发请求数和请求体大小有限制。默认配置可能无法支撑高并发文件上传,触发超时或资源耗尽。 4. **拦截器/过滤器干扰** 若自定义拦截器在 `MultipartResolver` 之前读取了请求体(如通过 `HttpServletRequest.getInputStream()`),会导致后续解析失败。 --- #### 解决方案 1. **确保单次解析请求** 在拦截器或过滤器中避免提前读取请求体。若必须读取,需包装请求为可重复读取的 `ContentCachingRequestWrapper`: ```java public class CacheRequestFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, ...) { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); chain.doFilter(wrappedRequest, response); } } ``` 2. **优化 MultipartResolver 配置** **使用 `StandardServletMultipartResolver`(推荐)**: 它基于 Servlet 3.0+ 标准,性能更优且线程安全。在 Spring Boot 中配置: ```yaml spring: servlet: multipart: enabled: true max-file-size: 10MB max-request-size: 100MB location: /tmp/uploads # 指定临时目录 ``` **或调整 `CommonsMultipartResolver`**: ```java @Bean public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver resolver = new CommonsMultipartResolver(); resolver.setMaxUploadSize(100_000_000); // 100MB resolver.setMaxInMemorySize(10_000); // 10KB(超出部分写临时文件) resolver.setUploadTempDir(new FileSystemResource("/tmp")); return resolver; } ``` 3. **提升系统资源** - **临时目录**:使用独立 SSD 磁盘存储临时文件,避免 I/O 瓶颈。 - **内存分配**:增加 JVM 堆内存(`-Xmx4g`)并监控 GC 情况。 - **容器调优**(Tomcat 示例): ```properties server.tomcat.max-threads=200 # 最大工作线程数 server.tomcat.max-swallow-size=2GB # 最大请求体大小 ``` 4. **异步处理上传** 将文件保存操作移至异步线程,减少请求线程阻塞时间: ```java @PostMapping("/upload") public CompletableFuture<String> upload(@RequestPart MultipartFile file) { return CompletableFuture.supplyAsync(() -> { storageService.save(file); // 异步保存 return "Success"; }, taskExecutor); } ``` 5. **分布式锁控制(极端场景)** 若涉及共享资源(如写入同一文件),使用 Redis 或 ZooKeeper 分布式锁[^3]: ```java try (DistributedLock lock = new DistributedLock("upload-lock", fileId)) { lock.acquire(); // 取锁 storageService.save(file); } // 自动释放锁 ``` --- #### 预防措施 - **压力测试**:使用 JMeter 模拟高并发上传,验证系统极限。 - **监控指标**:监控临时目录空间、JVM 内存、容器线程池状态。 - **熔断机制**:集成 Resilience4j 或 Hystrix,在资源不足时快速失败。 > 关键点:**避免重复解析请求体** + **确保资源充足**。若问题持续,检查是否有自定义过滤器提前消费了输入流,或升级 Spring/Servlet 容器版本。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值