这个通用下载代码有什么问题?


    public String download(HttpServletResponse response, String fileName) {
        File file = new File(filePath + '/' + fileName);
        if (!file.exists()) {
            return "下载文件不存在";
        }
        response.reset();
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setContentLength((int) file.length());
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));) {
            byte[] buff = new byte[1024];
            OutputStream os = response.getOutputStream();
            int i = 0;
            while ((i = bis.read(buff)) != -1) {
                os.write(buff, 0, i);
                os.flush();
            }
        } catch (IOException e) {
            log.error("文件下载失败:==>{}==>{}", fileName, e);
            return "下载失败";
        }
        return "下载成功";
    }

  1. 文件路径中可能存在恶意构造的这种相对路径:../ 需要清理。
  2. ContentType 不应该滥用application/octet-stream,应该详细的指明文件的ContentType。
  3. Content-Disposition 中的filename不能直接拼接文件名称,可能会导致乱码。
  4. 流复制代码冗余了,标准库自带。而且缓冲区仅1kb,太小了。
  5. 应该设置Content-Length头,客户端依赖它实现下载进度。
1 Like

可以参考这个.

	private String base = "D:\\";
	
	public long download(HttpServletRequest request, HttpServletResponse response, String file) throws IOException {
	
		Path path = Paths.get(base, file).normalize();
	
		// 文件不存在,或者目标文件是目录
		if (Files.notExists(path) || Files.isDirectory(path)) {
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
			return 0;
		}
	
		// 尝试获取文件的ContentType
		String mediaType = Files.probeContentType(path);
	
		if (mediaType == null) {
			// 获取失败,使用通用的二进制文件类型
			mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
		}
	
		// Content-Type
		response.setHeader(HttpHeaders.CONTENT_TYPE, mediaType);
		// Content-Disposition
		response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
				.filename(path.getFileName().toString(), StandardCharsets.UTF_8).build().toString());
		// Content-Length
		response.setContentLengthLong(Files.size(path));
	
		return Files.copy(path, response.getOutputStream());
	}
1 Like