@PathVariable对于特殊字符截断问题

解决下载文件名被截断的问题
本文介绍了一个关于使用Spring MVC下载文件时遇到的问题,即文件名在URL中被截断的情况。通过调整路径匹配规则,使用正则表达式确保文件名中的特定字符如点(.)、下划线(_)、破折号(-)等不被截断。

概述:

@ResponseBody
	@RequestMapping(value="/download/{fileName:[a-zA-Z0-9\\.\\-\\_]+}", method = RequestMethod.GET)
	public void downloadAmr( HttpServletRequest request, HttpServletResponse response, @PathVariable("fileName") String fileName) {
		response.setContentType("application/octet-stream");
		String dir = System.getProperty("catalina.home");  //获得tomcat所在的工作路径    
		System.out.println("tomcat路径=" + dir);  
		//获取到存储了文件存储位置的filedir.properties 文件路径  
		String dir2 = dir.substring(0, dir.length()) + File.separator +"webapps" + File.separator + "ROOT"  + File.separator + fileName;  
		File file = new File(dir2);
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];    
		int len;    
		try { 
			InputStream inputStream = new FileInputStream(file);
			while ((len = inputStream.read(buffer)) > -1 ) {    
				byteArrayOutputStream.write(buffer, 0, len);    
			}  
			byteArrayOutputStream.flush();  
			response.getOutputStream().write(byteArrayOutputStream.toByteArray());
		} catch (FileNotFoundException e) {
			logger.error("读取文件异常", e);
		} catch (IOException e) {  
			logger.error(e.getMessage(), e);  
		} 
		logger.info("下载进入。。。。。。。。。。。。。。。。。");
	}

总结:

1、默认值情况下 /download/{fileName}, 然后 @PathVariable("fileName"),

如果路径为/download/1.jpg的话,那么 fileName=1 而不是1.jpg,问题就是默认对于字符._-相关进行截断了。

2、解决方法就是{fileName:[a-zA-Z0-9\\.\\-\\_]+}  用正则表达式表示这些字符不能被截断。

package com.kucun.controller; import com.fasterxml.jackson.databind.ObjectMapper; import com.kucun.Service.AppService; import com.kucun.Service.DynamicRepositoryService; import com.kucun.data.entity.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import java.util.Date; import java.util.List; import java.util.Map; import javax.validation.Valid; @RestController @RequestMapping("/app") public class AppController { @Autowired private AppService appService; @Autowired private ObjectMapper objectMapper; private Map<String, Class<?>> ENTITY_MAP ; @Autowired public AppController( DynamicRepositoryService dynamicRepositoryService, AppService appService, ObjectMapper objectMapper ) { this.ENTITY_MAP = dynamicRepositoryService.getStringClassMap(); this.appService = appService; this.objectMapper = objectMapper; } // ====================== 数据查询 ====================== @GetMapping("/all") public Information getAllData( @RequestParam(value = "since",required=false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Date since ) { System.out.println("since:"+since); try { if(since == null) return appService.getAllData(); else return appService.getUpdatesSince(since); } catch (Exception e) { // TODO: handle exception return Information.NewFail(e.getMessage()); } } // 添加保存全部数据的API端点 @PostMapping("/save-all") public Information saveAllData(@RequestBody Map<String, List<?>> allData) { try { return appService.saveAllData(allData); } catch (Exception e) { return Information.NewFail("保存全部数据失败: " + e.getMessage()); } } @GetMapping("/all/{entityType}") public Information getEntityData(@PathVariable String entityType, @RequestParam(value = "since",required=false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Date since) { return appService.getEntityData(entityType.toLowerCase()); } // ====================== CRUD操作 ====================== @PostMapping("/add/{entityType}") public Information addEntity( @PathVariable String entityType, @RequestBody @Valid Map<String, Object> requestBody ) { return handleEntityOperation(entityType, requestBody, "add"); } @PostMapping("/select/{entityType}") public Information queryEntity( @PathVariable String entityType, @RequestBody Map<String, Object> requestBody ) { return handleEntityOperation(entityType, requestBody, "select"); } @PostMapping("/delete/{entityType}") public Information deleteEntity( @PathVariable String entityType, @RequestBody Map<String, Object> requestBody ) { return handleEntityOperation(entityType, requestBody, "delete"); } @PostMapping("/update/{entityType}") public Information updateEntity( @PathVariable String entityType, @RequestBody Map<String, Object> requestBody ) { return handleEntityOperation(entityType, requestBody, "update"); } // ====================== 核心辅助方法 ====================== private Information handleEntityOperation( String entityType, Map<String, Object> requestBody, String operation ) { String normalizedType = entityType.toLowerCase(); // 特殊处理 Dingdan_bancai if ("dingdan_bancai".equals(normalizedType)) { try { Dingdan_bancai entity = objectMapper.convertValue(requestBody, Dingdan_bancai.class); // 从请求体中提取用户ID并设置到临时字段 if (requestBody.containsKey("currentUserId")) { entity.setCurrentUserId(((Integer) requestBody.get("currentUserId"))); } return appService.handleDingdanBancaiOperation( entity, operation); } catch (Exception e) { return Information.NewFail(operation + "操作失败: " + e.getMessage()); } } Class<?> entityClass = ENTITY_MAP.get(normalizedType); if (entityClass == null) { return Information.NewFail("不支持的实体类型: " + entityType); } try { Object entity = objectMapper.convertValue(requestBody, entityClass); //System.out.println(Information.NewSuccess(requestBody).DataJson()); switch (operation) { case "add": return appService.addEntity(entity); case "select": return appService.queryEntity((EntityBasis) entity); case "delete": // 确保实体实现了EntityBasis接口 if (entity instanceof EntityBasis) { return appService.deleteEntity((EntityBasis) entity); } else { return Information.NewFail("删除操作需要实体实现EntityBasis接口"); } case "update": return appService.updateEntity(entity); default: return Information.NewFail("无效的操作类型"); } } catch (Exception e) { return Information.NewFail(operation + "操作失败: " + e.getMessage()); } } }Request URL: http://192.168.1.4:8080/app/save-all Request Method: POST Status Code: 404 Remote Address: 192.168.1.4:8080 Referrer Policy: strict-origin-when-cross-origin Connection: keep-alive Content-Language: zh-CN Content-Type: application/json Date: Thu, 31 Jul 2025 17:01:14 GMT Keep-Alive: timeout=60 Transfer-Encoding: chunked Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Accept: */* Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive Content-Length: 76274 content-type: application/json Host: 192.168.1.4:8080 Referer: https://servicewechat.com/wxfb2de4d38511c8cf/devtools/page-frame.html User-Agent: Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36 wechatdevtools/1.06.2504010 MicroMessenger/8.0.5 Language/zh_CN webview/ sessionid/14 {bancais: [{caizhi: {id: 1}, mupi1: {id: 1}, mupi2: {id: 1}, houdu: 15, kucun: null, id: 1,…},…],…} bancais: [{caizhi: {id: 1}, mupi1: {id: 1}, mupi2: {id: 1}, houdu: 15, kucun: null, id: 1,…},…] caizhis: [{name: "纤维板", id: 1, lastUpdated: "2025-06-29T19:58:33.000+00:00", deleted: false, deletedAt: null},…] chanpin_zujians: [{chanpin: {id: 1}, zujian: {id: 1}, bancai: {id: 1}, one_howmany: 20, zujianshu: null, id: 1,…},…] chanpins: [{dingdan_chanpin: [{id: 10}, {id: 14}], bianhao: "AN-1210", chanpin_zujian: [{id: 1}], id: 1,…},…] dingdan_bancais: [{id: 1, lastUpdated: "2025-06-29T13:50:38.000+00:00", deleted: false, deletedAt: null,…},…] dingdan_chanpins: [{dingdan: {id: 3}, chanpin: {id: 3}, shuliang: 200, id: 5,…},…] dingdans: [,…] jinhuos: [{dingdan_bancai: {id: 2}, shuliang: 42, date: "2025-07-01T07:47:35.000+00:00", user: {id: 1},…},…] kucuns: [,…] mupis: [{you: true, name: "桃花心", id: 1, lastUpdated: "2025-06-29T19:58:37.000+00:00", deleted: false,…},…] users: [,…] zujians: [{name: "侧板", chanping_zujian: [{id: 1}, {id: 2}, {id: 3}], id: 1,…},…] _lastModified: null _lastSync: "2025-07-31T17:01:10.572Z" 2025-08-01 01:01:29.902 INFO 19848 --- [io-8080-exec-10] com.kucun.aspect.LoggingAspect : Request URL: http://192.168.1.4:8080/app/save-all 2025-08-01 01:01:29.902 INFO 19848 --- [io-8080-exec-10] com.kucun.aspect.LoggingAspect : Request Method: POST 2025-08-01 01:01:29.903 INFO 19848 --- [io-8080-exec-10] com.kucun.aspect.LoggingAspect : Request Parameters: {"requestBody":"{}"} 2025-08-01 01:01:29.903 INFO 19848 --- [io-8080-exec-10] com.kucun.aspect.LoggingAspect : Response JSON: error
08-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值