大きなファイルをアップロードする必要がある場合は、次のロジックを考慮する必要があります。
大きなファイルをアップロードするには、通常、ファイルをスライス (チャンク) に分けてアップロードし、すべてのスライスが完全なファイルにマージされます。これは、次のロジックに従って実装できます:
フロントエンドは、ページにアップロードするファイルを選択し、Blob.slice メソッドを使用してファイルをスライスします。各スライスのサイズは固定値 (5MB など) であり、合計でスライスが何個あるかを記録します。
スライスをバックエンド サービスに個別にアップロードすると、XMLHttpRequest や Axios などのライブラリを使用して Ajax リクエストを送信できます。各スライスには、現在のスライス インデックス (0 から始まる)、スライスの総数、およびスライス ファイル データの 3 つのパラメータを含める必要があります。
バックエンド サービスはスライスを受信すると、指定されたパスの下の一時ファイルにスライスを保存し、アップロードされたスライス インデックスとアップロード ステータスを記録します。スライスのアップロードに失敗した場合は、フロントエンドにスライスを再送信するように通知されます。
すべてのスライスが正常にアップロードされると、バックエンド サービスはすべてのスライスの内容を読み取り、それらを完全なファイルにマージします。ファイルのマージは、java.io.SequenceInputStream と BufferedOutputStream を使用して実現できます。
最後に、ファイルアップロード成功の応答結果をフロントエンドに返します。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>File Upload</title> </head> <body> <input type="file" id="fileInput"> <button onclick="upload()">Upload</button> <script> function upload() { let file = document.getElementById("fileInput").files[0]; let chunkSize = 5 * 1024 * 1024; // 切片大小为5MB let totalChunks = Math.ceil(file.size / chunkSize); // 计算切片总数 let index = 0; while (index < totalChunks) { let chunk = file.slice(index * chunkSize, (index + 1) * chunkSize); let formData = new FormData(); formData.append("file", chunk); formData.append("index", index); formData.append("totalChunks", totalChunks); // 发送Ajax请求上传切片 $.ajax({ url: "/uploadChunk", type: "POST", data: formData, processData: false, contentType: false, success: function () { if (++index >= totalChunks) { // 所有切片上传完成,通知服务端合并文件 $.post("/mergeFile", {fileName: file.name}, function () { alert("Upload complete!"); }) } } }); } } </script> </body> </html>
コントローラー層:
@RestController public class FileController { @Value("${file.upload-path}") private String uploadPath; @PostMapping("/uploadChunk") public void uploadChunk(@RequestParam("file") MultipartFile file, @RequestParam("index") int index, @RequestParam("totalChunks") int totalChunks) throws IOException { // 以文件名+切片索引号为文件名保存切片文件 String fileName = file.getOriginalFilename() + "." + index; Path tempFile = Paths.get(uploadPath, fileName); Files.write(tempFile, file.getBytes()); // 记录上传状态 String uploadFlag = UUID.randomUUID().toString(); redisTemplate.opsForList().set("upload:" + fileName, index, uploadFlag); // 如果所有切片已上传,则通知合并文件 if (isAllChunksUploaded(fileName, totalChunks)) { sendMergeRequest(fileName, totalChunks); } } @PostMapping("/mergeFile") public void mergeFile(String fileName) throws IOException { // 所有切片均已成功上传,进行文件合并 List<File> chunkFiles = new ArrayList<>(); for (int i = 0; i < getTotalChunks(fileName); i++) { String chunkFileName = fileName + "." + i; Path tempFile = Paths.get(uploadPath, chunkFileName); chunkFiles.add(tempFile.toFile()); } Path destFile = Paths.get(uploadPath, fileName); try (OutputStream out = Files.newOutputStream(destFile); SequenceInputStream seqIn = new SequenceInputStream(Collections.enumeration(chunkFiles)); BufferedInputStream bufIn = new BufferedInputStream(seqIn)) { byte[] buffer = new byte[1024]; int len; while ((len = bufIn.read(buffer)) > 0) { out.write(buffer, 0, len); } } // 清理临时文件和上传状态记录 for (int i = 0; i < getTotalChunks(fileName); i++) { String chunkFileName = fileName + "." + i; Path tempFile = Paths.get(uploadPath, chunkFileName); Files.deleteIfExists(tempFile); redisTemplate.delete("upload:" + chunkFileName); } } private int getTotalChunks(String fileName) { // 根据文件名获取总切片数 return Objects.requireNonNull(Paths.get(uploadPath, fileName).toFile().listFiles()).length; } private boolean isAllChunksUploaded(String fileName, int totalChunks) { // 判断所有切片是否已都上传完成 List<String> uploadFlags = redisTemplate.opsForList().range("upload:" + fileName, 0, -1); return uploadFlags != null && uploadFlags.size() == totalChunks; } private void sendMergeRequest(String fileName, int totalChunks) { // 发送合并文件请求 new Thread(() -> { try { URL url = new URL("http://localhost:8080/mergeFile"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); OutputStream out = conn.getOutputStream(); String query = "fileName=" + fileName; out.write(query.getBytes()); out.flush(); out.close(); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); while (br.readLine() != null) ; br.close(); } catch (IOException e) { e.printStackTrace(); } }).start(); } @Autowired private RedisTemplate<String, Object> redisTemplate; }
このうち、file.upload-pathは保存ファイルのアップロード パスは application.properties または application.yml で設定できます。同時に、アップロード状況を記録するために RedisTemplate の Bean を追加する必要があります。
RedisTemplate を使用する必要がある場合は、次のパッケージを導入する必要があります
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
同時に yml
spring.redis.host=localhost spring.redis.port=6379 spring.redis.database=0
@Component public class myClass { @Autowired private RedisTemplate<String, Object> redisTemplate; public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } public Object get(String key) { return redisTemplate.opsForValue().get(key); } }
以上がvue+springboot を使用して大きなファイルをアップロードする方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。