Si vous devez télécharger des fichiers volumineux, vous devez considérer la logique suivante :
Les téléchargements de fichiers volumineux nécessitent généralement le fichier à découper (chunk) ) avant de fusionner toutes les tranches en un fichier complet. Il peut être implémenté selon la logique suivante :
Le front-end sélectionne le fichier à télécharger sur la page et utilise la méthode Blob.slice pour découper le fichier . Généralement, la taille de chaque tranche est une valeur fixe (par exemple 5 Mo) et enregistrez le nombre de tranches au total.
Téléchargez les tranches sur le service backend séparément, vous pouvez utiliser des bibliothèques telles que XMLHttpRequest ou Axios pour envoyer des requêtes Ajax. Pour chaque tranche, trois paramètres doivent être inclus : l'index de tranche actuel (à partir de 0), le nombre total de tranches et les données du fichier de tranche.
Une fois que le service backend a reçu la tranche, il l'enregistre dans un fichier temporaire sous le chemin spécifié et enregistre l'index de la tranche téléchargée et l'état de téléchargement. Si une tranche ne parvient pas à être téléchargée, le frontal est invité à retransmettre la tranche.
Lorsque toutes les tranches sont téléchargées avec succès, le service backend lit le contenu de toutes les tranches et les fusionne dans un fichier complet. La fusion de fichiers peut être réalisée à l'aide de java.io.SequenceInputStream et BufferedOutputStream.
Enfin, renvoyez le résultat de la réponse du téléchargement réussi du fichier au front-end.
<!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>
couche contrôleur :
@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; }
Parmi eux, file.upload-path est le chemin d'enregistrement du téléchargement de fichiers, qui peut être configuré dans application.properties ou application.yml. Dans le même temps, vous devez ajouter le Bean of RedisTemplate pour enregistrer l'état de téléchargement.
Si vous devez utiliser RedisTemplate, vous devez introduire le package suivant
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
En même temps, configurez les informations Redis dans yml
spring.redis.host=localhost spring.redis.port=6379 spring.redis.database=0# 🎜🎜#Ensuite, utilisez-le comme ça dans votre propre classe
@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); } }Notes
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!