You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中客户端-服务器架构导入CSV文件遇GUI冻结问题

客户端GUI冻结+CSV文件网络导入问题修复

问题背景

开发客户端-服务器架构的商城商品CSV导入功能时,客户端选择文件路径后出现GUI冻结,无法进行任何操作。当前客户端和服务端代码如下:

客户端原代码

public void importFromCSV(String path) {
    
    try {
        writer.write(path);
        writer.flush();
        int bytes = 0;
        File file = new File(path);
        FileInputStream fis = new FileInputStream(path);
        
        // send file size
        dos.writeLong(file.length());
        // break file into chunks
        byte[] buffer = new byte[4 * 1024];
        while ((bytes = fis.read(buffer)) != -1) {
            dos.write(buffer, 0, bytes);
            dos.flush();
        }
        fis.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

服务端原代码

public static void importCSV(Store store) {
    try {
        int bytes = 0;
        System.out.println("about to read path");
        String path = reader.readLine();
        System.out.println("read path");
        
        FileOutputStream fos = new FileOutputStream(path);

        long size = dis.readLong();     // read file size
        byte[] buffer = new byte[4*1024];
        while (size > 0 && (bytes = dis.read(buffer, 0, (int)Math.min(buffer.length, size))) != -1) {
            fos.write(buffer, 0, bytes);
            size -= bytes;      // read upto file size
        }
        fos.close();

        store.setProducts(CSVFileWork.CSVToProducts(path));
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println(store.getProducts().size());
}

核心问题分析

  1. GUI主线程阻塞:客户端的importFromCSV方法直接在GUI线程执行,文件读取、网络传输都是同步阻塞操作,会卡住Swing/JavaFX的事件分发线程,导致界面冻结无响应。
  2. 传输协议不完整:客户端用writer.write(path)发送路径后没有添加换行符,服务端reader.readLine()会一直等待换行标记,导致客户端和服务端互相阻塞,加重GUI冻结。
  3. 异常处理缺失:仅打印栈轨迹,没有中断错误流程或通知用户,问题发生后无法及时感知。
  4. 服务端安全风险:直接使用客户端传入的路径写入文件,存在路径遍历攻击风险,且未验证服务端路径是否存在。

修复方案

客户端修复要点

  • 将文件读取和网络操作移至后台线程,避免阻塞GUI主线程;
  • 发送路径后添加换行符,确保服务端能正确读取;
  • 完善异常处理,通过弹窗提示用户导入状态;
  • 关闭流时使用try-with-resources自动管理,避免资源泄漏。

修复后的客户端代码:

// 使用SwingWorker在后台执行,适配Swing GUI场景
public void importFromCSV(String path) {
    new SwingWorker<Void, Void>() {
        @Override
        protected Void doInBackground() throws Exception {
            // 先验证文件有效性
            File csvFile = new File(path);
            if (!csvFile.exists() || !csvFile.isFile()) {
                JOptionPane.showMessageDialog(null, "文件不存在或不是有效文件");
                return null;
            }

            try (FileInputStream fis = new FileInputStream(csvFile);
                 BufferedWriter bufferedWriter = new BufferedWriter(writer)) {
                // 发送路径时添加换行,让服务端readLine能正常读取
                bufferedWriter.write(path);
                bufferedWriter.newLine();
                bufferedWriter.flush();

                // 发送文件大小
                dos.writeLong(csvFile.length());
                dos.flush();

                byte[] buffer = new byte[4 * 1024];
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    dos.write(buffer, 0, bytesRead);
                    dos.flush();
                }
                JOptionPane.showMessageDialog(null, "文件传输完成");
            } catch (IOException e) {
                JOptionPane.showMessageDialog(null, "导入失败:" + e.getMessage());
                e.printStackTrace();
            }
            return null;
        }
    }.execute();
}

服务端修复要点

  • 读取路径时处理可能的读取失败;
  • 服务端指定固定存储目录,不要直接使用客户端传入的路径,避免安全风险;
  • 验证传输的文件大小,确保接收完整;
  • 同样使用try-with-resources管理流资源;
  • 异常时输出明确错误信息,便于排查。

修复后的服务端代码:

// 服务端固定存储目录,避免路径遍历攻击
private static final String UPLOAD_DIR = "./server_uploads/";

public static void importCSV(Store store) {
    try {
        // 确保上传目录存在
        File uploadDir = new File(UPLOAD_DIR);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }

        System.out.println("about to read path");
        String clientPath = reader.readLine();
        if (clientPath == null) {
            System.err.println("未读取到客户端路径");
            return;
        }
        System.out.println("read path: " + clientPath);

        // 仅提取文件名,避免客户端传入恶意路径
        String fileName = new File(clientPath).getName();
        String serverFilePath = UPLOAD_DIR + fileName;

        try (FileOutputStream fos = new FileOutputStream(serverFilePath)) {
            long fileSize = dis.readLong();
            byte[] buffer = new byte[4 * 1024];
            long remainingSize = fileSize;
            int bytesRead;

            while (remainingSize > 0 && (bytesRead = dis.read(buffer, 0, (int) Math.min(buffer.length, remainingSize))) != -1) {
                fos.write(buffer, 0, bytesRead);
                remainingSize -= bytesRead;
            }

            if (remainingSize != 0) {
                System.err.println("文件传输不完整,预期大小:" + fileSize + ",实际接收:" + (fileSize - remainingSize));
                return;
            }

            // 解析CSV商品数据
            store.setProducts(CSVFileWork.CSVToProducts(serverFilePath));
            System.out.println("导入完成,商品数量:" + store.getProducts().size());
        }
    } catch (Exception e) {
        System.err.println("导入失败:" + e.getMessage());
        e.printStackTrace();
    }
}

额外建议

  • 客户端可以添加进度条,通过SwingWorker的publish方法实时更新传输进度;
  • 服务端可以对上传的CSV文件做格式校验,避免解析错误;
  • 网络传输可添加校验和机制,确保文件完整性。

内容的提问来源于stack exchange,提问作者Nathan428

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 13:15:30