sping boot(Jackson)中Long转换为JSON数值溢出的处理

当Java的Long类型超过JavaScript的Number最大值时,会导致序列化后的值不一致。为避免此问题,可以通过在VO和DTO中将Long类型序列化为JSON字符串。一种方法是在Long属性上添加Jackson的序列化注解,另一种是全局配置Jackson,使所有Long自动转为String。如果需要某些Long属性仍为JSONnumber,可以使用特定注解进行标记。

由于JavaScript中的number最大值为9007199254740992(最大安全值),而java中Long最大为9223372036854775807L,所以在使用VO,DTO中为Long类型超过js最大值是就会出现值不一致的问题,一般的解决办法是把Long类型序列化成json string类型

方式一: 在VO中Long类型属性增加序列化注解

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; 
......   
    // 如果是long需要序列化为String,避免JSON数字溢出
    @JsonSerialize(using= ToStringSerializer.class)
    private Long id;

方式二:定义一个通用的Long序列化处理器

/*
 * Copyright (c) huggingai(huangguoliang)  copyright message.   2022-2122.   All rights reserved.
 */

package com.huggingai.ai.taskmanager.config;

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 所有的Long自动转换为String,编码json最大值溢出
 *
 * @author hank
 */
@Configuration
public class LongToStringAutoConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
                .serializerByType(Long.TYPE, ToStringSerializer.instance);
    }

}

注意: 使用这种方式所有的VO,DTO中的Long都将序列化为json的string类型,如果某些Long属性需要序列化为json number型需要添加序列化注解:

/*
 * Copyright (c) huggingai(huangguoliang)  copyright message.   2022-2122.   All rights reserved.
 */

package com.huggingai.common.entity.vo;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

/**
 * 分页信息
 *
 * @author hank
 * @since 2023/1/1 11:58
 */
@ApiModel
@Data
public class PageVO<T> {
    @ApiModelProperty(value = "总记录数")
    @JsonSerialize(as = Number.class)
    private Long total;

    @ApiModelProperty(value = "数据列表")
    private List<T> records;

    @ApiModelProperty(value = "当前页码")
    @JsonSerialize(as = Number.class)
    private Long current;

    @ApiModelProperty(value = "每页条数")
    @JsonSerialize(as = Number.class)
    private Long size;

    @ApiModelProperty(value = "总页数")
    @JsonSerialize(as = Number.class)
    private Long pages;
}

<think>我们正在讨论Spring Boot中使用GET请求获取文件的方法。用户的需求是了解如何实现这一功能。 根据Spring Boot的特性,我们可以通过几种方式实现: 1. 使用`ResourceHttpRequestHandler`来处理静态文件请求。 2. 使用`ResponseEntity`返回文件资源。 3. 通过静态资源映射,但通常用于直接访问静态目录下的文件,而这里可能需要动态处理。 由于用户要求使用GET请求,我们可以设计一个控制器方法,映射到某个GET请求的URL,然后返回文件。 步骤: a. 确保文件存在,如果不存在则返回404。 b. 读取文件,并设置正确的响应头(如Content-Type, Content-Disposition等)。 c. 将文件内容写入响应输出流。 另外,如果文件是静态资源(比如存放在classpath:static/或指定目录),可以直接通过URL访问,但这里用户可能希望有控制逻辑(如权限校验),所以推荐使用控制器方法。 示例代码: 假设我们要实现一个下载文件的接口,URL为`/download`,通过参数指定文件名。 注意:在实际项目中,需要对文件名进行安全校验,防止路径穿越攻击(例如使用`FilenameUtils`或`Path`来规范化路径)。 我们将使用`Resource`接口来表示文件资源,并使用`ResponseEntity`来构建响应。 代码示例: ```java import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.net.MalformedURLException; import java.nio.file.Path; import java.nio.file.Paths; @RestController public class FileDownloadController { private final String FILE_DIRECTORY = "/path/to/your/files"; // 替换为你的文件目录 @GetMapping("/download") public ResponseEntity<Resource> downloadFile(@RequestParam("filename") String filename) throws MalformedURLException { // 构建文件路径 Path filePath = Paths.get(FILE_DIRECTORY).resolve(filename).normalize(); Resource resource = new UrlResource(filePath.toUri()); // 检查文件是否存在且可读 if (resource.exists() && resource.isReadable()) { // 设置响应头 HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\""); // 根据文件类型设置Content-Type,这里设置为二进制流(自动类型) return ResponseEntity.ok() .headers(headers) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(resource); } else { // 文件不存在 return ResponseEntity.notFound().build(); } } } ``` 注意: 1. 文件目录`FILE_DIRECTORY`需要根据实际情况修改。 2. 上述代码中,我们使用了`UrlResource`,也可以使用`FileSystemResource`。 3. 为了安全,这里应该对文件名进行校验,防止路径穿越。例如,确保文件路径在指定的目录下。可以使用以下方法校验: ```java if (!filePath.startsWith(FILE_DIRECTORY)) { // 路径非法,返回错误 return ResponseEntity.badRequest().build(); } ``` 但是注意:上面的`FILE_DIRECTORY`需要是`Path`对象,并且使用绝对路径。上面的校验方法在代码中需要调整,因为`filePath`已经是绝对路径,而`FILE_DIRECTORY`也是绝对路径,我们可以这样: ```java Path basePath = Paths.get(FILE_DIRECTORY).toAbsolutePath().normalize(); Path targetPath = basePath.resolve(filename).normalize(); if (!targetPath.startsWith(basePath)) { // 非法路径 return ResponseEntity.badRequest().body("Invalid filename"); } ``` 因此,改进后的代码: ```java @RestController public class FileDownloadController { private final Path basePath = Paths.get("/path/to/your/files").toAbsolutePath().normalize(); @GetMapping("/download") public ResponseEntity<Resource> downloadFile(@RequestParam("filename") String filename) throws MalformedURLException { // 解析文件名并标准化 Path targetPath = basePath.resolve(filename).normalize(); // 检查路径是否在基础路径内(防止路径穿越) if (!targetPath.startsWith(basePath)) { return ResponseEntity.badRequest().build(); } Resource resource = new UrlResource(targetPath.toUri()); if (resource.exists() && resource.isReadable()) { HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\""); return ResponseEntity.ok() .headers(headers) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(resource); } else { return ResponseEntity.notFound().build(); } } } ``` 这样,我们就实现了一个安全的文件下载接口。 另外,如果文件是存储在数据库中(如BLOB字段),则可以从数据库读取,然后通过`InputStreamResource`(注意:`InputStreamResource`不会自动关闭流,所以需要自己管理)或者使用`ByteArrayResource`。 但是,使用数据库存储时,通常我们会有文件的元数据(如文件名,类型等),可以更好地设置响应头。 然而,用户的问题中并未说明文件存储方式,所以我们提供了文件系统的示例。 综上所述,Spring Boot中通过GET请求获取文件的主要步骤是: 1. 接收请求参数(文件名或其他标识) 2. 根据参数获取文件资源(注意安全校验) 3. 设置响应头(特别是Content-Disposition,用于指定下载行为) 4. 返回`ResponseEntity<Resource>` 最后,生成相关问题。 </think>### Spring Boot中使用GET请求获取文件的实现方法 在Spring Boot中,通过GET请求获取文件主要有三种实现方式: #### 1. **静态资源映射(最简单)** 适用于直接访问`resources/static`或`resources/public`目录下的文件: ```java // application.properties配置 spring.mvc.static-path-pattern=/files/** spring.web.resources.static-locations=classpath:/custom-files/ ``` 访问URL:`http://localhost:8080/files/test.pdf` #### 2. **ResponseEntity动态返回(推荐)** 通过控制器动态处理文件下载: ```java import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; @GetMapping("/download") public ResponseEntity<Resource> downloadFile(@RequestParam String fileName) { Path filePath = Paths.get("/storage/" + fileName).normalize(); Resource resource = new UrlResource(filePath.toUri()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"") .body(resource); } ``` 访问URL:`http://localhost:8080/download?fileName=document.zip` #### 3. **使用InputStreamResource(大文件优化)** 适合流式传输大文件: ```java @GetMapping(value = "/stream", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public InputStreamResource streamFile() throws IOException { InputStream in = new FileInputStream("/large/video.mp4"); return new InputStreamResource(in); } ``` ### 📌 关键注意事项 1. **安全验证**:添加`@PreAuthorize`注解进行权限控制 ```java @PreAuthorize("hasRole('USER')") ``` 2. **路径安全**:防止目录穿越攻击 ```java // 在Path解析后添加校验 if (!filePath.startsWith("/safe-directory")) { throw new AccessDeniedException(); } ``` 3. **HTTP缓存**:设置缓存头提高性能 ```java .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS)) ``` 4. **文件类型**:准确设置Content-Type ```java .contentType(MediaType.APPLICATION_PDF) ``` ### 性能优化建议 - 对于超过100MB的大文件,使用`InputStreamResource`避免内存溢出 - 启用Gzip压缩:`server.compression.enabled=true` - 使用`Range`头支持断点续传 > 当需要动态生成文件(如报表)时,推荐使用`ResponseEntity`方案;静态资源直接映射更高效。实际选择需结合具体场景[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值