React Fetch调用Java REST API下载PDF无法在Adobe打开的问题
我在Postman中测试过该API,PDF可正常渲染,确认API功能正常。但通过React代码使用Fetch下载PDF时,Adobe提示错误:“Adobe Acrobat无法打开该文件,因为它不是支持的文件类型或文件已损坏”。下载的文件大小恰好为1kb,说明未正确从API获取Blob。查阅网上示例但未发现问题,相关代码如下:
React代码
const downloadFile = async uploadId => { const response = await callFetch("/uploads/download/" + uploadId + "?officerId=" + officerId, "GET", ""); if (response.status === 401 || response.status === 403) { alert("Error " + response.status); sessionStorage.clear(); return; } const file = response.blob(); const url = URL.createObjectURL( new Blob([file], {type:"application/pdf"}) ); const filename = response.headers.get("Content-Disposition").split("filename=")[1]; const link = document.createElement('a'); link.href = url; link.setAttribute( 'download', filename, ); document.body.appendChild(link); link.click(); link.parentNode.removeChild(link); URL.revokeObjectURL(url); }; const callFetch = (endpoint, method, jsonStr) => { let myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Accept","application/json"); return callFetchApi(endpoint, method, jsonStr, myHeaders); }; const callFetchApi = (endpoint, method, data, myHeaders) => { const serverName = "http://localhost:8080/AuxPolice/api"; myHeaders.append("Access-Control-Allow-Credentials", 'true'); myHeaders.append("Access-Control-Allow-Origin", '*'); const jwt = sessionStorage.getItem("jwt"); let headerJwt = "Bearer " + jwt; if (jwt != null) { myHeaders.append("Authorization", headerJwt); } let myInit = { method: method, headers: myHeaders }; let url = serverName + endpoint; if (data) { myInit.body = data; } return fetch(url, myInit); };
Java后端代码
@GetMapping(value = "/download" + "/{id}") public ResponseEntity<Resource> downloadGet(@PathVariable Long id, @RequestParam Long officerId) throws SQLException { Officer loggedInOfficer = this.auxPoliceService.getOfficer(officerId); Upload paramRec = new Upload(); paramRec.setUploadId(id); Upload download = auxPoliceService.getUploads(loggedInOfficer.getOfficerId(), paramRec).get(0); Blob blob = download.getBlob(); byte [] bytes = blob.getBytes(1, (int)blob.length()); blob.free(); InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(bytes)); String filename = download.getFilename(); String contentType = "application/pdf"; HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION); headers.add("Content-Disposition", "inline; filename=" + filename); return ResponseEntity.ok() .headers(headers) .contentLength(bytes.length) .contentType(MediaType.parseMediaType(contentType)) .body(resource); }
异步方法未等待:
response.blob()是异步Promise方法,必须加await才能拿到实际的Blob对象。当前代码直接把Promise对象传入Blob构造函数,生成的是包含Promise的无效Blob,这是文件损坏的核心原因。修改为:const file = await response.blob();请求头Accept设置错误:
callFetch中设置了Accept: application/json,但API返回的是PDF二进制数据,这个头会让Fetch尝试将响应解析为JSON,导致二进制数据损坏。将Accept改为application/pdf,或者直接删除该头。冗余的Blob构造:
response.blob()已经返回了正确的PDF Blob,无需再用new Blob([file], {type:"application/pdf"})包裹,直接使用file即可:const url = URL.createObjectURL(file);错误的CORS请求头:
callFetchApi中手动添加的Access-Control-Allow-Credentials和Access-Control-Allow-Origin是服务器响应头,客户端请求时不需要设置,直接删除这两行代码,避免干扰请求。文件名解析鲁棒性不足:
Content-Disposition的文件名可能带引号(如filename="xxx.pdf"),直接用split会拿到带引号的字符串。建议用正则提取:const disposition = response.headers.get("Content-Disposition"); const filename = disposition?.match(/filename="?([^"]+)"?/)?.[1] || "download.pdf";
内容的提问来源于stack exchange,提问作者Pat

