6

I have A big file and i want to upload that in Server side. it's very important when occured any problem (like interrupting the internet or power cut ...) if i retry to upload, file uploaded from resume and doesn't need to send file from beginning.

I try this approach with sending file chunks but it seems that's not a good way, because a send chunks(byte arrays) directly in response Entity and this isn't good idea.

whatever if anybody can develop this approach and make this code a better code with better performance i appreciate that. does anybody known Best practice way to doing that??

and if u like my code, vote me thanks :)

RestController

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

Service

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

Sending chunks with 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!!!");
    }
}
hamed
  • 114
  • 2
  • 2
  • 9

0 Answers0