杰瑞科技汇

Java在Linux下文件上传如何实现?

核心概念

  1. 后端: 运行在 Linux 服务器上的 Java 应用(使用 Spring Boot, Servlet),它负责接收 HTTP 请求中的文件数据,并将其保存到服务器的磁盘上。
  2. 前端/客户端: 可以是网页、移动 App 或其他服务,它负责将本地文件打包成一个 HTTP 请求,并发送给后端。
  3. HTTP 请求: 文件上传通常使用 POST 方法,并设置 Content-Typemultipart/form-data,这是一种特殊的编码格式,允许在一个请求中同时发送文本数据和文件数据。

使用 Spring Boot (推荐,现代且高效)

Spring Boot 是目前 Java 后端开发的主流框架,它极大地简化了文件上传的实现。

项目准备

创建一个 Spring Boot 项目,你可以使用 Spring Initializr 来快速生成项目。

  • Project: Maven Project
  • Language: Java
  • Spring Boot: 选择一个稳定版本 (如 3.x.x)
  • Project Metadata: 填写你的 Group, Artifact, Name 等。
  • Dependencies: 添加 Spring Web

点击 "Generate" 下载项目,并用你喜欢的 IDE (如 IntelliJ IDEA 或 VS Code) 打开。

编写后端代码

src/main/java/your/package/controller 目录下,创建一个控制器类 FileUploadController.java

package com.example.demo.controller;
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.UUID;
@RestController
@RequestMapping("/api/upload")
public class FileUploadController {
    // 定义一个固定的上传目录,为了方便,我们放在项目根目录下的 "uploads" 文件夹
    // 在 Linux 系统中,确保这个目录有写入权限
    private final String UPLOAD_DIR = "/path/to/your/project/uploads/"; // !!! 重要:请修改为你的实际路径
    // 确保上传目录存在
    public FileUploadController() {
        try {
            Files.createDirectories(Paths.get(UPLOAD_DIR));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 处理单个文件上传
     * @param file 前端传来的文件,由 MultipartFile 自动封装
     * @return 返回上传结果信息
     */
    @PostMapping("/single")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return ResponseEntity.badRequest().body("请选择一个文件上传");
        }
        try {
            // 为了防止文件名冲突,使用 UUID 生成新的文件名
            String originalFilename = file.getOriginalFilename();
            String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
            String newFilename = UUID.randomUUID().toString() + fileExtension;
            // 构建文件在服务器上的完整保存路径
            Path destinationPath = Paths.get(UPLOAD_DIR + newFilename);
            // 将文件内容写入到指定路径
            Files.copy(file.getInputStream(), destinationPath);
            System.out.println("文件上传成功: " + destinationPath.toString());
            return ResponseEntity.ok("文件上传成功: " + newFilename);
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.internalServerError().body("文件上传失败: " + e.getMessage());
        }
    }
    /**
     * 处理多个文件上传
     * @param files 前端传来的文件数组
     * @return 返回上传结果信息
     */
    @PostMapping("/multiple")
    public ResponseEntity<String> uploadFiles(@RequestParam("files") MultipartFile[] files) {
        if (files == null || files.length == 0) {
            return ResponseEntity.badRequest().body("请选择至少一个文件上传");
        }
        int successCount = 0;
        int failCount = 0;
        for (MultipartFile file : files) {
            if (file.isEmpty()) {
                failCount++;
                continue;
            }
            try {
                String originalFilename = file.getOriginalFilename();
                String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
                String newFilename = UUID.randomUUID().toString() + fileExtension;
                Path destinationPath = Paths.get(UPLOAD_DIR + newFilename);
                Files.copy(file.getInputStream(), destinationPath);
                successCount++;
                System.out.println("文件上传成功: " + destinationPath.toString());
            } catch (IOException e) {
                failCount++;
                System.err.println("文件上传失败: " + file.getOriginalFilename() + " - " + e.getMessage());
            }
        }
        return ResponseEntity.ok("上传完成,成功: " + successCount + ", 失败: " + failCount);
    }
}

配置 (重要)

a) 设置上传文件大小限制

默认情况下,Spring Boot 对上传文件的大小有限制(通常是 1MB),你需要在 application.propertiesapplication.yml 文件中进行配置。

src/main/resources/application.properties

# 单个文件最大大小 (10MB)
spring.servlet.multipart.max-file-size=10MB
# 总请求最大大小 (包含多个文件和表单数据) (11MB)
spring.servlet.multipart.max-request-size=11MB

b) 确保上传目录权限

这是在 Linux 环境下最关键的一步!你的 Java 应用运行在某个用户下(tomcat, root, 或普通用户),该用户必须对上传目录有写权限

假设你的上传目录是 /var/www/uploads,并且你的 Java 应用由 tomcat 用户运行。

在 Linux 终端中执行:

# 1. 创建目录 (如果不存在)
sudo mkdir -p /var/www/uploads
# 2. 将目录的所有权赋予运行 Java 应用的用户 (tomcat)
sudo chown -R tomcat:tomcat /var/www/uploads
# 3. 设置适当的权限 (755 表示所有者有读写执行权限,组和其他用户有读和执行权限)
sudo chmod -R 755 /var/www/uploads

注意: 如果你使用的是 Spring Boot 内嵌的 Tomcat 服务器,它通常以当前启动用户(可能是你的登录用户)的身份运行,你需要根据实际情况调整 chown 命令中的用户。

运行和测试

  1. 运行你的 Spring Boot 应用。
  2. 你可以使用 curl 命令来测试上传功能。

测试单个文件上传:

# 假设你的应用运行在 8080 端口
curl -X POST -F "file=@/path/to/your/local/test.txt" http://localhost:8080/api/upload/single

测试多个文件上传:

curl -X POST -F "files=@/path/to/your/local/file1.txt" -F "files=@/path/to/your/local/file2.jpg" http://localhost:8080/api/upload/multiple

使用传统 Servlet API

如果你不使用 Spring Boot,或者在一个传统的 Java Web 项目(如部署在 Tomcat, Jetty 中)中,可以使用 Servlet 3.0+ 原生的 API。

后端代码 (UploadServlet.java)

package com.example.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
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.UUID;
@WebServlet("/api/upload/servlet")
@MultipartConfig(
        fileSizeThreshold = 1024 * 1024,      // 1 MB
        maxFileSize = 10 * 1024 * 1024,      // 10 MB
        maxRequestSize = 11 * 1024 * 1024     // 11 MB
)
public class UploadServlet extends HttpServlet {
    private final String UPLOAD_DIR = "/path/to/your/webapp/uploads/"; // !!! 重要:请修改为你的实际路径
    @Override
    public void init() throws ServletException {
        super.init();
        try {
            Files.createDirectories(Paths.get(UPLOAD_DIR));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取 Part 对象,对应于前端表单中的 <input type="file" name="file">
        Part filePart = req.getPart("file");
        if (filePart == null || filePart.getSize() == 0) {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "No file uploaded");
            return;
        }
        String originalFilename = filePart.getSubmittedFileName();
        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
        String newFilename = UUID.randomUUID().toString() + fileExtension;
        Path destinationPath = Paths.get(UPLOAD_DIR + newFilename);
        // 使用 Part 的 write 方法直接写入文件
        // 注意:write 方法的路径是相对于服务器根目录的,或者使用绝对路径
        // 为了清晰,我们使用绝对路径
        filePart.write(destinationPath.toString());
        System.out.println("文件上传成功: " + destinationPath.toString());
        resp.getWriter().write("File uploaded successfully: " + newFilename);
    }
}

配置

a) web.xml 配置 (可选,如果使用注解则不需要)

<servlet>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>com.example.servlet.UploadServlet</servlet-class>
    <multipart-config>
        <file-size-threshold>1048576</file-size-threshold> <!-- 1MB -->
        <max-file-size>10485760</max-file-size> <!-- 10MB -->
        <max-request-size>11534336</max-request-size> <!-- 11MB -->
    </multipart-config>
</servlet>
<servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/api/upload/servlet</url-pattern>
</servlet-mapping>

b) 文件大小限制

  • 在 Servlet 3.0 中,可以通过 @MultipartConfig 注解或 web.xml 中的 <multipart-config> 来设置。
  • 如果使用 Tomcat,还可以在 conf/context.xmlconf/server.xml 中为整个主机或上下文设置:
    <Context>
        <Manager pathname="" />
        <Resources allowLinking="true" />
        <!-- 设置上传限制 -->
        <multipart-config>
            <max-file-size>10485760</max-file-size>
            <max-request-size>11534336</max-request-size>
            <file-size-threshold>1048576</file-size-threshold>
        </multipart-config>
    </Context>

c) 目录权限 与 Spring Boot 方案相同,确保运行 Tomcat 的用户对上传目录有写权限。


前端调用示例 (HTML + JavaScript)

这是一个简单的 HTML 页面,用于选择文件并发送到后端。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">文件上传测试</title>
</head>
<body>
    <h1>单个文件上传</h1>
    <form action="http://localhost:8080/api/upload/single" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <button type="submit">上传</button>
    </form>
    <hr>
    <h1>多个文件上传</h1>
    <form action="http://localhost:8080/api/upload/multiple" method="post" enctype="multipart/form-data">
        <input type="file" name="files" multiple>
        <button type="submit">上传</button>
    </form>
    <hr>
    <h1>使用 JavaScript (AJAX) 上传</h1>
    <input type="file" id="jsFileInput">
    <button id="jsUploadBtn">上传</button>
    <p id="jsStatus"></p>
    <script>
        document.getElementById('jsUploadBtn').addEventListener('click', function() {
            const fileInput = document.getElementById('jsFileInput');
            const status = document.getElementById('jsStatus');
            const file = fileInput.files[0];
            if (!file) {
                status.textContent = '请先选择一个文件';
                return;
            }
            const formData = new FormData();
            formData.append('file', file); // 'file' 必须与后端 @RequestParam("file") 的名字一致
            status.textContent = '上传中...';
            fetch('http://localhost:8080/api/upload/single', {
                method: 'POST',
                body: formData
            })
            .then(response => response.text())
            .then(data => {
                status.textContent = data;
            })
            .catch(error => {
                status.textContent = '上传失败: ' + error;
                console.error('Error:', error);
            });
        });
    </script>
</body>
</html>

Linux 环境下的重要注意事项

  1. 磁盘空间: 确保 Linux 服务器的 /var 或你指定的上传分区有足够的磁盘空间。
  2. 文件名和路径安全:
    • 永远不要信任客户端传来的文件名,恶意用户可能提交 ../../../etc/passwd 这样的路径,尝试覆盖系统文件。
    • 始终使用安全的文件名,如上面代码中使用的 UUID,这样可以防止文件名冲突和恶意路径注入。
    • 不要将文件保存在 Web 根目录下,否则文件可能被直接通过 URL 访问,最好保存在 Web 根目录之外的、非公开的目录中。
  3. 权限管理: 如前所述,仔细设置文件和目录的权限 (chmod, chown),遵循最小权限原则,只给运行应用的用户必要的写入权限。
  4. 病毒扫描: 对于生产环境,上传的文件应经过病毒扫描,特别是当这些文件会被其他用户下载时。
  5. 日志记录: 详细记录文件上传的日志,包括上传时间、用户、文件名、文件大小、IP地址等,便于审计和排查问题。
  6. 性能考虑: 上传大文件会消耗大量内存和 I/O,对于非常大的文件,考虑实现分块上传断点续传功能,这通常需要前端和后端协同配合。
分享:
扫描分享到社交APP
上一篇
下一篇