erosynthesis

erosynthesis

springboot + minioを使用したファイルアップロード

良い、以下は Spring Boot と MinIO を使用してファイルの分割アップロード、再開アップロード、秒伝を実現するサンプルコードです:

@RestController
@RequestMapping("/file")
public class FileController {

    private final MinioClient minioClient;

    @Autowired
    public FileController(MinioClient minioClient) {
        this.minioClient = minioClient;
    }

    @PostMapping("/upload")
    public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) throws Exception {
        String md5 = DigestUtils.md5Hex(file.getInputStream());
        String objectName = md5 + "/" + file.getOriginalFilename();
        boolean objectExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucket").build());
        if (!objectExists) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket("my-bucket").build());
        }
        if (minioClient.objectExists(ObjectExistsArgs.builder().bucket("my-bucket").object(objectName).build())) {
            return ResponseEntity.ok("秒伝成功");
        }
        int partSize = 5 * 1024 * 1024;
        long objectSize = file.getSize();
        int partCount = (int) Math.ceil(objectSize / (double) partSize);
        List<PartETag> partETags = new ArrayList<>(partCount);
        InputStream inputStream = file.getInputStream();
        for (int i = 0; i < partCount; i++) {
            long offset = i * partSize;
            long size = Math.min(partSize, objectSize - offset);
            UploadPartArgs uploadPartArgs = UploadPartArgs.builder()
                    .bucket("my-bucket")
                    .object(objectName)
                    .uploadId(md5)
                    .partNumber(i + 1)
                    .partSize(size)
                    .stream(inputStream, size, offset)
                    .build();
            partETags.add(minioClient.uploadPart(uploadPartArgs));
        }
        CompleteMultipartUploadArgs completeMultipartUploadArgs = CompleteMultipartUploadArgs.builder()
                .bucket("my-bucket")
                .object(objectName)
                .uploadId(md5)
                .partETags(partETags)
                .build();
        minioClient.completeMultipartUpload(completeMultipartUploadArgs);
        return ResponseEntity.ok("アップロード成功");
    }

    @GetMapping("/download")
    public ResponseEntity<InputStreamResource> download(@RequestParam("filename") String filename) throws Exception {
        String md5 = DigestUtils.md5Hex(new ByteArrayInputStream(filename.getBytes()));
        String objectName = md5 + "/" + filename;
        boolean objectExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucket").build());
        if (!objectExists) {
            return ResponseEntity.notFound().build();
        }
        boolean objectPartExists = minioClient.objectExists(ObjectExistsArgs.builder().bucket("my-bucket").object(objectName).build());
        if (!objectPartExists) {
            return ResponseEntity.notFound().build();
        }
        GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                .bucket("my-bucket")
                .object(objectName)
                .build();
        InputStream inputStream = minioClient.getObject(getObjectArgs);
        InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
                .contentLength(inputStream.available())
                .body(inputStreamResource);
    }

    @GetMapping("/resume")
    public ResponseEntity<String> resume(@RequestParam("file") MultipartFile file) throws Exception {
        String md5 = DigestUtils.md5Hex(file.getInputStream());
        String objectName = md5 + "/" + file.getOriginalFilename();
        boolean objectExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucket").build());
        if (!objectExists) {
            return ResponseEntity.notFound().build();
        }
        boolean objectPartExists = minioClient.objectExists(ObjectExistsArgs.builder().bucket("my-bucket").object(objectName).build());
        if (!objectPartExists) {
            return ResponseEntity.notFound().build();
        }
        int partSize = 5 * 1024 * 1024;
        long objectSize = file.getSize();
        int partCount = (int) Math.ceil(objectSize / (double) partSize);
        List<PartETag> partETags = new ArrayList<>(partCount);
        InputStream inputStream = file.getInputStream();
        for (int i = 0; i < partCount; i++) {
            long offset = i * partSize;
            long size = Math.min(partSize, objectSize - offset);
            UploadPartArgs uploadPartArgs = UploadPartArgs.builder()
                    .bucket("my-bucket")
                    .object(objectName)
                    .uploadId(md5)
                    .partNumber(i + 1)
                    .partSize(size)
                    .stream(inputStream, size, offset)
                    .build();
            partETags.add(minioClient.uploadPart(uploadPartArgs));
        }
        CompleteMultipartUploadArgs completeMultipartUploadArgs = CompleteMultipartUploadArgs.builder()
                .bucket("my-bucket")
                .object(objectName)
                .uploadId(md5)
                .partETags(partETags)
                .build();
        minioClient.completeMultipartUpload(completeMultipartUploadArgs);
        return ResponseEntity.ok("再開成功");
    }

}

上記のコードでは、MinioClientを使用して MinIO サーバーに接続し、ファイルのアップロード、ダウンロード、再開機能を実現しています。

ファイルをアップロードする際、まずファイルの MD5 値を計算し、その MD5 値をフォルダー名として使用し、アップロードされたファイルをそのフォルダーに保存します。もしそのファイルが MinIO サーバーに既に存在する場合は、直接秒伝成功を返します。そうでなければ、ファイルをいくつかのパーツに分割してアップロードし、最後にパーツを結合して完全なファイルにします。

ファイルをダウンロードする際、まずファイルの MD5 値を計算し、その MD5 値をフォルダー名として、そこから指定されたファイルをダウンロードします。

ファイルを再開する際、まずファイルの MD5 値を計算し、そのファイルが MinIO サーバーに既に存在するかを確認します。存在する場合は、アップロードファイルの方法に従って再開します。存在しない場合は、ファイルが存在しないというエラーメッセージを返します。

実際の使用時には、コード内のmy-bucketを自分のバケット名に置き換え、MinIO サーバーが正しく設定され、起動されていることを確認する必要があります。

良い、以下はファイルの分割アップロードと再開アップロード機能を実現するシンプルな Vue3 コードの例です。

<template>
  <div>
    <input type="file" @change="onFileChange" />
    <button @click="upload">アップロード</button>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      file: null,
      uploaded: 0,
      total: 0,
      progress: 0,
      chunkSize: 1024 * 1024,
      chunks: [],
      uploadedChunks: [],
    };
  },
  methods: {
    onFileChange(event) {
      this.file = event.target.files[0];
      this.total = this.file.size;
      const chunksCount = Math.ceil(this.total / this.chunkSize);
      for (let i = 0; i < chunksCount; i++) {
        const start = i * this.chunkSize;
        const end = Math.min(start + this.chunkSize, this.total);
        const chunk = this.file.slice(start, end);
        this.chunks.push(chunk);
        this.uploadedChunks.push(false);
      }
    },
    async upload() {
      const config = {
        onUploadProgress: (progressEvent) => {
          this.uploaded = progressEvent.loaded;
          this.progress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
        },
      };
      const url = 'http://localhost:8080/upload';
      for (let i = 0; i < this.chunks.length; i++) {
        if (this.uploadedChunks[i]) {
          continue;
        }
        const formData = new FormData();
        formData.append('file', this.chunks[i]);
        formData.append('index', i.toString());
        formData.append('chunksCount', this.chunks.length.toString());
        await axios.post(url, formData, config);
        this.uploadedChunks[i] = true;
      }
      console.log('アップロード完了');
    },
  },
};
</script>

この例では、axios ライブラリを使用してファイルのアップロードリクエストを送信しています。onFileChangeメソッドでは、ファイルを複数の同じサイズのチャンクに分割し、それらをchunks配列に保存しています。また、どのチャンクがアップロード完了したかを追跡するためにuploadedChunks配列を作成しています。

uploadメソッドでは、チャンク配列をループして、各チャンクをサーバーにアップロードするために FormData オブジェクトを使用します。アップロード中に、onUploadProgressコールバックを使用してアップロード進捗を追跡し、uploadedprogress変数を更新します。

ここでのコードはあくまでサンプルであり、具体的なニーズに応じて修正する必要があります。より詳細なコード例やチュートリアルが必要な場合は、お知らせください。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。