注册

springboot + minio + kkfile实现文件预览(不暴露minio地址)

前言



之前我写过一片文章【springboot + minio + kkfile实现文件预览】,该文章介绍了如何使用kkfile预览文件,但是文章中介绍的方案,会暴露minio的地址,实际的预览地址如下:


http://kkfile-server/onlinePreview?url=base64UrlEncode(minio生成的文件预览地址)

但是大多数情况下,minio服务的地址是不允许暴露的,所有我们对其进行优化,依然使用kkfile预览文件,但是我们使用文件流的方式,并且在下载接口上校验用户认证的有效性,在保证不暴露minio地址的前提下,还加入了token认证,提高了安全性,话不多说,直接上代码。



一、文件上传


上传服务


public void uploadFile(MultipartFile file) throws Exception {
String fileName = System.currentTimeMillis() + "-" + file.getOriginalFilename();
PutObjectArgs args = PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
client.putObject(args);
}

封装接口


@PostMapping("upload")
public RestResult upload(MultipartFile file) {
try {
sysFileService.uploadFile(file);
} catch (Exception e) {
log.error("上传文件失败", e);
return RestResult.fail(e.getMessage());
}
}

二、文件下载


下载服务


public void download(String filename, HttpServletResponse response) throws ServiceException {
try {
InputStream inputStream = client.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filename).build());


// 设置响应头信息,告诉前端浏览器下载文件
response.setContentType("application/octet-stream;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));

// 获取输出流进行写入数据
OutputStream outputStream = response.getOutputStream();
// 将输入流复制到输出流
byte[] buffer = new byte[4096];
int bytesRead = -1;

while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
// 关闭流资源
inputStream.close();
outputStream.close();
} catch (Exception e) {
log.error("文件下载失败:" + e.getMessage());
throw new ServiceException("文件下载失败");
}
}

封装接口


@ApiOperation("文件下载")
@GetMapping("/download/{token}/{filename}")
public void getDownload(@PathVariable("token") String token, @PathVariable("filename") String filename, HttpServletResponse response) {
tokenUtils.validateToken(token);
sysFileService.download(filename, response);
}


上面的接口有两个地方需要注意



  1. @GetMapping("/download/{token}/{filename}")中filename参数必须放在最后
  2. tokenUtils.validateToken(token);
    该接口要在拦截器中放行,验证token在代码逻辑中,这里根据项目中实际场景去实现。该地址为kkfile请求获取文件流的地址,所以需要放开鉴权


三、文件预览地址获取


文件预览地址生成服务(该服务只是获取token并拼接到文件下载地址中,不对token做验证,因为该服务的接口在请求进入前要做校验)


public String getPreviewUrl(String filename) throws UnsupportedEncodingException {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = sra.getRequest();
if (request ==null || StringUtils.isBlank(request.getHeader(TokenConstants.AUTHENTICATION))) {
throw new ServiceException("未获取到有效token");
}
String previewUrl = filePreviewUrl + FileUploadUtils.base64UrlEncode(fileDownloadUrl + "/" + token + "/" + filename);
return previewUrl + "&fullfilename=" + URLEncoder.encode(filename, "UTF-8");
}

FileUploadUtils中的base64UrlEncode方法


public static String base64UrlEncode(String url) throws UnsupportedEncodingException {
String base64Url = Base64.getEncoder().encodeToString(url.getBytes(StandardCharsets.UTF_8));
return URLEncoder.encode(base64Url, "UTF-8");
}

封装接口,获取文件预览地址


@GetMapping("/getPreviewUrl")
public RestResult<String> getPreviewUrl(String filename) throws UnsupportedEncodingException {
return RestResult.ok(sysFileService.getPreviewUrl(filename));
}

测试


假设



  1. 文件服务地址为:http://file-server
  2. kkfile服务地址为:http://kkfile-server
  3. 文件名称为:xxxx.docx

最后生成的文件预览地址为:


http://kkfile-server/onlinePreview?url=aHR0cDovLzE3Mi4xNi41MC4y....&fullfilename=xxxx.docx

其中aHR0cDovLzE3Mi4xNi41MC4y....为:


FileUploadUtils.base64UrlEncode("http://file-server" + "/" + token + "/" + filename);


截图为证


image.png




作者:小太阳381
来源:juejin.cn/post/7424338056918761498

0 个评论

要回复文章请先登录注册