Spring 3使用CommonsMultipartResolver上传文件后下载异常求助
嘿,我来帮你捋捋这个问题——你上传时打印的[B@6b02547c只是字节数组的内存地址,不是实际内容,这个本身完全没问题。但下载后Word提示内容错误,核心原因肯定是文件在上传、存储或下载环节中字节数据被损坏了,要么是数据不完整,要么是被错误编码了。下面给你一步步排查和修复的方案:
1. 先确认上传时的字节读取是否完整
首先要确保你完整读取了上传文件的所有字节,而不是只拿到了部分数据。你可以在上传时打印字节数组的长度,和原文件的实际大小对比下:
CommonsMultipartFile file = ...; // 你的上传文件对象 byte[] fileBytes = file.getBytes(); // 打印字节长度,和原文件大小对比 System.out.println("读取到的字节长度:" + fileBytes.length);
如果长度和原文件不一致,大概率是CommonsMultipartResolver的上传大小限制截断了文件。检查下你的配置:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 全局最大上传大小,比如设为10MB --> <property name="maxUploadSize" value="10485760"/> <!-- 单个文件的最大大小,建议和上面保持一致或更大 --> <property name="maxUploadSizePerFile" value="10485760"/> </bean>
要是你的原文件超过这个限制,就会被截断,导致存储的字节不完整,下载后自然打不开。
2. 检查数据库的字段类型是否正确
PostgreSQL存储二进制文件必须用bytea类型,绝对不能用text或者varchar这类字符类型——字符类型会对二进制数据做编码转换,直接破坏文件的原始字节。看看你的表结构是不是这样:
CREATE TABLE file_storage ( id SERIAL PRIMARY KEY, file_name VARCHAR(255) NOT NULL, file_data BYTEA NOT NULL, -- 这里必须是bytea! content_type VARCHAR(100) -- 建议存储文件的MIME类型,方便下载时设置响应头 );
如果之前用了字符类型,赶紧改成bytea,重新上传测试。
3. 修复下载时的字节输出逻辑
下载环节最容易踩坑的地方是输出流没有完全写入,或者响应头设置错误。给你一个正确的下载示例:
@RequestMapping("/download") public void downloadFile(HttpServletResponse response) throws IOException { // 从数据库获取文件数据和元信息 byte[] fileBytes = ...; // 从数据库查询出的bytea数据 String fileName = "abc.docx"; // 对应正确的MIME类型:docx用下面这个,pdf用"application/pdf" String contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; // 设置关键响应头 response.setContentType(contentType); // 处理中文文件名乱码问题 response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\""); // 必须设置正确的内容长度,让浏览器知道文件的完整大小 response.setContentLength(fileBytes.length); // 写入输出流,确保所有字节都发送出去 ServletOutputStream out = response.getOutputStream(); out.write(fileBytes); out.flush(); out.close(); }
重点注意:
- 一定要设置
Content-Length,否则浏览器可能会提前截断输出 - 写入后要
flush()并close()输出流,确保所有字节都被发送 - MIME类型要对应正确,不然浏览器可能无法正确识别文件格式
4. 验证字节数据的一致性(可选但推荐)
如果上面的步骤都没问题,你可以做个MD5校验:上传前计算原文件的MD5值,存储到数据库;下载后计算下载文件的MD5,对比是否一致。如果不一致,说明中间某个环节篡改了数据。
// 计算字节数组的MD5值 public static String calculateMD5(byte[] data) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] hashBytes = md.digest(data); StringBuilder sb = new StringBuilder(); for (byte b : hashBytes) { sb.append(String.format("%02x", b)); } return sb.toString(); }
上传时把原文件的MD5存到数据库,下载后计算下载文件的MD5,对比相同就说明数据是完整的,不同的话就顺着上传→存储→下载的流程一步步排查。
按照上面的步骤逐一排查,应该就能解决文件损坏的问题了!
内容的提问来源于stack exchange,提问作者Bharath Pateru

