Maison  >  Article  >  Java  >  Comment utiliser vue+springboot pour télécharger des fichiers volumineux

Comment utiliser vue+springboot pour télécharger des fichiers volumineux

王林
王林avant
2023-05-16 09:43:111204parcourir

Logic

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.

frontend

<!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>

backend

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.

Configuration de RedisTemplate

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

  • Vous devez contrôler la taille de chaque tranche téléchargée pour prendre en compte la vitesse de téléchargement et la stabilité pour éviter d'occuper trop de ressources du serveur ou de provoquer un échec de téléchargement en raison de l'instabilité du réseau.

  • Il existe une séquence pour télécharger les tranches. Vous devez vous assurer que toutes les tranches sont téléchargées avant de les fusionner. Sinon, des fichiers incomplets ou des erreurs de fusion de fichiers peuvent survenir.

  • Une fois le téléchargement terminé, les fichiers temporaires doivent être nettoyés à temps pour éviter les pannes de serveur causées par une utilisation trop importante d'espace disque. Vous pouvez configurer une tâche périodique pour nettoyer les fichiers temporaires expirés.

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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer