杰瑞科技汇

Java jQuery上传文件如何实现?

核心概念

  1. 前端: 使用 HTML 创建表单,使用 jQuery 来捕获用户操作,并通过 AJAX 将文件数据发送到服务器。
  2. 后端: 使用 Java 技术栈(如 Servlet 或 Spring Boot)来接收前端传来的文件数据,并将其保存到服务器上。

传统表单提交 (Form Submission)

这是最简单、最兼容的方式,但缺点是页面会刷新或跳转。

前端代码 (HTML & jQuery)

这里我们使用 jQuery 来提交表单,可以做一些简单的验证。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">传统文件上传</title>
    <!-- 引入 jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <h2>传统方式上传文件</h2>
    <form id="uploadForm" action="upload" method="post" enctype="multipart/form-data">
        <!-- 
            注意:form 的 enctype 必须是 "multipart/form-data"
            才能正确上传文件
        -->
        <input type="file" name="file" id="fileInput">
        <button type="submit">上传</button>
    </form>
    <div id="message" style="margin-top: 10px; color: green;"></div>
    <script>
        $(document).ready(function() {
            $('#uploadForm').on('submit', function(e) {
                // 可以在这里添加一些简单的验证
                if ($('#fileInput').val() == '') {
                    $('#message').text('请先选择一个文件!');
                    e.preventDefault(); // 阻止表单提交
                    return;
                }
                // 使用 jQuery 的 serialize() 方法来序列化表单数据
                // 但注意:serialize() 对于文件上传可能不是最佳选择,
                // 因为它不能直接处理文件对象,这里我们让浏览器自己处理。
                // 我们只是阻止默认行为,然后手动触发提交。
                // 如果需要显示进度,这里会更复杂,通常用 AJAX。
                // 这里我们直接让表单按默认方式提交。
                // 服务器响应后,页面会刷新或跳转。
            });
        });
    </script>
</body>
</html>

后端代码 (Java - Servlet)

使用传统的 Java Servlet 来接收文件。

Maven 依赖 (pom.xml)

<dependencies>
    <!-- Servlet API -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- Apache Commons FileUpload, 用于简化文件上传处理 -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>
    <!-- Commons IO, 用于辅助操作 -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

Servlet 代码 (UploadServlet.java)

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
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 java.io.File;
import java.io.IOException;
import java.util.List;
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    // 上传文件存储目录
    private static final String UPLOAD_DIRECTORY = "uploads";
    // 上传配置
    private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3;  // 3MB
    private static final int MAX_FILE_SIZE = 1024 * 1024 * 40;    // 40MB
    private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 检查是否为 multipart/form-data 请求
        if (!ServletFileUpload.isMultipartContent(request)) {
            // 如果不是,则停止处理
            response.getWriter().println("Error: 表单必须是 multipart/form-data 类型");
            return;
        }
        // 配置上传参数
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
        factory.setSizeThreshold(MEMORY_THRESHOLD);
        // 设置临时存储目录
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 设置最大文件上传值
        upload.setFileSizeMax(MAX_FILE_SIZE);
        // 设置最大请求值 (包含文件和表单数据)
        upload.setSizeMax(MAX_REQUEST_SIZE);
        // 构建临时路径来存储上传的文件
        // 这个路径应该根据你的应用服务器配置来定
        String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
        // 如果目录不存在则创建
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
        try {
            // 解析请求的内容提取文件数据
            @SuppressWarnings("unchecked")
            List<FileItem> formItems = upload.parseRequest(request);
            if (formItems != null && formItems.size() > 0) {
                for (FileItem item : formItems) {
                    // 处理不在表单中的字段
                    if (!item.isFormField()) {
                        String fileName = new File(item.getName()).getName();
                        String filePath = uploadPath + File.separator + fileName;
                        File storeFile = new File(filePath);
                        // 在控制台输出上传文件的信息
                        System.out.println("文件上传到: " + filePath);
                        // 保存文件到硬盘
                        item.write(storeFile);
                    }
                }
            }
            // 上传成功,重定向到成功页面或返回成功信息
            request.setAttribute("message", "文件上传成功!");
            getServletContext().getRequestDispatcher("/message.jsp").forward(request, response);
        } catch (Exception ex) {
            request.setAttribute("message", "错误信息: " + ex.getMessage());
            getServletContext().getRequestDispatcher("/message.jsp").forward(request, response);
        }
    }
}

AJAX 异步上传 (推荐)

这种方式用户体验更好,页面不会刷新,可以显示上传进度,并在上传完成后动态更新页面内容。

前端代码 (HTML & jQuery)

关键点:

  • 使用 FormData 对象来构建要发送的数据,它可以正确地包含文件。
  • 设置 AJAX 请求的 processDatacontentTypefalse
  • 使用 xhr 对象来监听上传进度。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">AJAX 文件上传</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <style>
        #progressBar { width: 300px; height: 20px; background-color: #f3f3f3; border-radius: 5px; }
        #progressBar div { width: 0%; height: 100%; background-color: #4CAF50; border-radius: 5px; text-align: center; line-height: 20px; color: white; }
    </style>
</head>
<body>
    <h2>AJAX 方式上传文件</h2>
    <input type="file" id="fileInput">
    <button id="uploadButton">上传</button>
    <div id="progressBar">
        <div id="progress"></div>
    </div>
    <div id="message" style="margin-top: 10px;"></div>
    <script>
        $(document).ready(function() {
            $('#uploadButton').on('click', function() {
                var fileInput = $('#fileInput')[0];
                var file = fileInput.files[0];
                if (!file) {
                    $('#message').text('请先选择一个文件!').css('color', 'red');
                    return;
                }
                // 创建 FormData 对象
                var formData = new FormData();
                formData.append('file', file); // 'file' 必须与后端接收的参数名一致
                // 禁用上传按钮,防止重复提交
                $(this).prop('disabled', true);
                $.ajax({
                    url: 'ajaxUpload', // Servlet 的映射路径
                    type: 'POST',
                    data: formData,
                    // 必须设置为 false,因为 jQuery 默认会将数据转换为 query string
                    processData: false,
                    // 必须设置为 false,因为 jQuery 默认会设置 Content-Type
                    contentType: false,
                    // 上传进度
                    xhr: function() {
                        var xhr = new window.XMLHttpRequest();
                        xhr.upload.addEventListener('progress', function(e) {
                            if (e.lengthComputable) {
                                var percent = Math.round((e.loaded / e.total) * 100);
                                $('#progress').css('width', percent + '%').text(percent + '%');
                            }
                        }, false);
                        return xhr;
                    },
                    success: function(response) {
                        $('#message').text(response.message).css('color', 'green');
                        // 清空文件输入
                        $('#fileInput').val('');
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        $('#message').text('上传失败: ' + textStatus).css('color', 'red');
                    },
                    complete: function() {
                        // 重新启用上传按钮
                        $('#uploadButton').prop('disabled', false);
                    }
                });
            });
        });
    </script>
</body>
</html>

后端代码 (Java - Servlet)

后端代码与方案一基本相同,只需要修改一下 Servlet 的映射路径 (@WebServlet) 和返回给前端的数据格式(通常是 JSON)。

Servlet 代码 (AjaxUploadServlet.java)

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.google.gson.Gson; // 引入 Gson 库来转换 JSON
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 java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@WebServlet("/ajaxUpload")
public class AjaxUploadServlet extends HttpServlet {
    private static final String UPLOAD_DIRECTORY = "uploads";
    private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3;
    private static final int MAX_FILE_SIZE = 1024 * 1024 * 40;
    private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50;
    // 使用 Gson 来处理 JSON
    private Gson gson = new Gson();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 设置响应内容类型为 JSON
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        Map<String, String> resultMap = new HashMap<>();
        if (!ServletFileUpload.isMultipartContent(request)) {
            resultMap.put("success", "false");
            resultMap.put("message", "表单必须是 multipart/form-data 类型");
            response.getWriter().write(gson.toJson(resultMap));
            return;
        }
        // ... (中间的文件处理逻辑与方案一的 UploadServlet 完全相同) ...
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(MEMORY_THRESHOLD);
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setFileSizeMax(MAX_FILE_SIZE);
        upload.setSizeMax(MAX_REQUEST_SIZE);
        String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
        try {
            @SuppressWarnings("unchecked")
            List<FileItem> formItems = upload.parseRequest(request);
            if (formItems != null && formItems.size() > 0) {
                for (FileItem item : formItems) {
                    if (!item.isFormField()) {
                        String fileName = new File(item.getName()).getName();
                        String filePath = uploadPath + File.separator + fileName;
                        File storeFile = new File(filePath);
                        item.write(storeFile);
                    }
                }
            }
            resultMap.put("success", "true");
            resultMap.put("message", "文件上传成功!");
        } catch (Exception ex) {
            resultMap.put("success", "false");
            resultMap.put("message", "错误信息: " + ex.getMessage());
        }
        // 将 Map 转换为 JSON 字符串并返回
        response.getWriter().write(gson.toJson(resultMap));
    }
}

Maven 依赖 (需要添加 Gson)

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.9</version>
</dependency>

Spring Boot 实现文件上传 (更现代的方式)

如果你使用的是 Spring Boot,处理文件上传会变得非常简单。

前端代码

前端代码与方案二的 AJAX 完全相同,无需改动。

后端代码 (Spring Boot)

Maven 依赖 (pom.xml)

Spring Boot Web starter 已经包含了处理文件上传所需的核心依赖。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Controller 代码 (FileUploadController.java)

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@RestController
public class FileUploadController {
    // 定义一个上传目录,在实际项目中最好配置在 application.properties 中
    private static final String UPLOADED_FOLDER = "uploads/";
    @PostMapping("/springUpload")
    public ResponseEntity<?> handleFileUpload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return new ResponseEntity<>("请选择一个文件", HttpStatus.BAD_REQUEST);
        }
        try {
            // 创建上传目录
            Path uploadPath = Paths.get(UPLOADED_FOLDER);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }
            // 获取文件名并构建目标路径
            String fileName = file.getOriginalFilename();
            Path filePath = uploadPath.resolve(fileName);
            // 保存文件
            Files.copy(file.getInputStream(), filePath);
            Map<String, String> response = new HashMap<>();
            response.put("success", "true");
            response.put("message", "文件上传成功: " + fileName);
            response.put("path", filePath.toString());
            return ResponseEntity.ok(response);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseEntity<>("上传失败: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

总结与对比

特性 传统表单 AJAX + Servlet Spring Boot
用户体验 差,页面会刷新或跳转 ,无刷新,可显示进度 ,无刷新,可显示进度
实现复杂度 简单 中等,需要处理 FormDataxhr 非常简单,Spring Boot 封装得很好
后端技术 原生 Servlet 原生 Servlet Spring Boot / Spring MVC
数据返回 HTML 页面重定向 JSON JSON
适用场景 简单、旧项目,对用户体验要求不高 大多数 Web 应用,推荐使用 基于 Spring 生态的现代项目

推荐选择

  • 对于新项目,强烈推荐 方案三 (Spring Boot),因为它最简洁、功能最强大。
  • 如果你不使用 Spring,或者需要在一个旧的非 Spring 项目中添加文件上传功能,方案二 (AJAX + Servlet) 是最佳选择。
分享:
扫描分享到社交APP
上一篇
下一篇