Backend Development 9 min read

Secure File Upload, Download, and Preview with Spring Boot, MinIO, and kkfile

This article explains how to securely upload files to MinIO, generate token‑protected download links, and create kkfile preview URLs using Spring Boot, while avoiding exposure of the MinIO address and adding token authentication for enhanced security.

Top Architect
Top Architect
Top Architect
Secure File Upload, Download, and Preview with Spring Boot, MinIO, and kkfile

In the original post the author describes a problem where the direct MinIO file preview URL ( http://kkfile-server/onlinePreview?url=base64UrlEncode ) exposes the MinIO service address, which is undesirable in most production environments.

The solution replaces the plain URL with a token‑protected file‑stream approach: files are uploaded to MinIO, then downloaded through a Spring Boot controller that validates a token before streaming the file to the client, preventing direct access to the MinIO endpoint.

File Upload Service

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);
}

Upload Endpoint

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

File Download Service

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;
        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("文件下载失败");
    }
}

Download Endpoint (token validation)

@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);
}

Two important notes: the @GetMapping("/download/{token}/{filename}") must place the filename parameter last, and the token validation logic should be placed in an interceptor that allows this specific path.

Preview URL Generation

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");
}

The helper method for Base64 URL encoding:

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

An example preview link generated by the service looks like:

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

Overall, the article provides a complete backend implementation that secures file access, avoids exposing MinIO endpoints, and integrates with kkfile for seamless document preview.

backendSpring BootFile UploadMinIOtoken authenticationkkfile
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.