Spring boot中分块上传大文件的最佳实践

ham*_*med 6 java file-upload backend chunks spring-boot

我有一个大文件,我想将其上传到服务器端。当发生任何问题(例如互联网中断或断电...)时,如果我重试上传,从简历上传的文件并且不需要从头开始发送文件,这一点非常重要。

我尝试使用这种方法发送文件,但这似乎不是一个好方法,因为直接在响应实体中发送块(字节数组),这不是一个好主意。

不管怎样,如果有人可以开发这种方法并使该代码成为具有更好性能的更好代码,我对此表示赞赏。有人知道这样做的 最佳实践方法吗?

如果你喜欢我的代码,请投票给我,谢谢:)

休息控制器

@RestController
@RequestMapping("/files")
public class Controller {

@Autowired
private MyService service;

@PutMapping("/upload/resume")
public Mono<ResponseEntity> uploadWithResume(@RequestPart("chunk")byte[] chunk,
                                             @RequestPart("fileName")String fileName,
                                             @RequestParam("length")Long length
) throws ParseException {
    try {
        return service.fileResumeUpload(chunk, fileName, length);
    } catch (IOException e) {
        e.printStackTrace();
        return Mono.just(ResponseEntity.status(HttpStatus.PERMANENT_REDIRECT).build());
    }
}

@RequestMapping(value = "/get/uploaded/size", method = RequestMethod.HEAD)
public Mono<ResponseEntity> getUploadedSize(@RequestParam("fileName") String fileName) throws IOException {
    if (Files.exists(Paths.get("src/main/resources/" + fileName))) {

        String size = String.valueOf(Files.size(Paths.get("src/main/resources/" + fileName)));

        return Mono.just(ResponseEntity.ok()
                .header("upload-offset", size)
                .build());
    } else{
        return Mono.just(ResponseEntity.notFound()
                .header("upload-offset" , "0").build());
    }
}
}
Run Code Online (Sandbox Code Playgroud)

服务

public Mono<ResponseEntity> fileResumeUpload(byte[] chunk , String fileName,long length) throws IOException, ParseException {
    BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("src/main/resources/" + fileName, true));

    boolean uploaded = true;

    try {
        out.write(chunk);
    } catch (IOException e) {
        uploaded = false;
        System.err.println("io exception");
    } finally {
        if (uploaded) {
            out.close();
                return Mono.just(ResponseEntity.ok()
                        .header("expiration-date", getExpirationDate())
                        .build());
        } else {
            out.close();
            return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用 webTestClient 发送块

  @Test
public void test1_upload_Expected_200StatusCode(){
    try {

        String fileName = "film.mkv";

        RandomAccessFile raf = new RandomAccessFile(new File("src/test/resources/" + fileName), "rw");
        long realSize = raf.length();
        List<String> strings = webTestClient.head().uri("/files/get/uploaded/size?fileName=" + fileName)
                .exchange().expectBody().returnResult().getResponseHeaders().get("upload-offset");

        long uploadedSize = Long.valueOf(strings.get(0));

        boolean f = false;

        int sizeBuffer = 256 * 1024;
        byte[] buffer = new byte[sizeBuffer];

        MultiValueMap<String, Object> formData;

        WebTestClient.ResponseSpec exchange = null;

        System.out.println("first uploaded Size ; " + uploadedSize);

        raf.seek(uploadedSize);
        while (raf.read(buffer) != -1) {
            formData = new LinkedMultiValueMap<>();
            formData.add("fileName", fileName);
            formData.add("chunk", buffer);
            formData.add("length", realSize);

            exchange = webTestClient.put().uri("/files/upload/resume")
                    .contentType(MediaType.MULTIPART_FORM_DATA)
                    .body(BodyInserters.fromMultipartData(formData))
                    .exchange();
            exchange.expectStatus().isOk();
            if (exchange.expectBody().returnResult().getStatus().is5xxServerError()) {
                return;
            }


            if (uploadedSize + 256 * 1024 > realSize) {
                sizeBuffer = ((int) (realSize - uploadedSize));
                System.out.println(sizeBuffer);
                uploadedSize = uploadedSize + sizeBuffer;
                System.out.println(uploadedSize);
                buffer = new byte[sizeBuffer];
                f=true;
            } else uploadedSize = uploadedSize + sizeBuffer;

            if (f) System.out.println(uploadedSize);
            //System.out.println(uploadedSize);

            float percent = ((float) uploadedSize / realSize * 100);
            System.out.format("%.2f\n", percent);
        }
        if (exchange!=null)
            exchange.expectStatus().isOk();
    }
    catch (Exception e){
        e.printStackTrace();
        System.err.println("channel closed!!!");
    }
}
Run Code Online (Sandbox Code Playgroud)