Heim  >  Artikel  >  Java  >  So verwenden Sie Vue + Springboot zum Hochladen großer Dateien

So verwenden Sie Vue + Springboot zum Hochladen großer Dateien

王林
王林nach vorne
2023-05-16 09:43:111205Durchsuche

Logik

Wenn Sie große Dateien hochladen müssen, sollten Sie die folgende Logik berücksichtigen:

  • Das Hochladen großer Dateien erfordert im Allgemeinen das Hochladen von Dateiabschnitten (Chunks) und das anschließende Zusammenführen aller Abschnitte zu vollständigen Dateien. Es kann gemäß der folgenden Logik implementiert werden:

  • Das Frontend wählt die Datei aus, die auf die Seite hochgeladen werden soll, und verwendet die Blob.slice-Methode, um die Datei zu segmentieren. Im Allgemeinen ist die Größe jedes Slice ein fester Wert (z. B. 5 MB) und die Gesamtgröße wird aufgezeichnet.

  • Laden Sie die Slices einzeln auf den Backend-Dienst hoch. Sie können Bibliotheken wie XMLHttpRequest oder Axios verwenden, um Ajax-Anfragen zu senden. Für jedes Slice müssen drei Parameter enthalten sein: aktueller Slice-Index (beginnend bei 0), Gesamtzahl der Slices und Slice-Dateidaten.

  • Nachdem der Backend-Dienst das Slice empfangen hat, speichert er es in einer temporären Datei unter dem angegebenen Pfad und zeichnet den hochgeladenen Slice-Index und den Upload-Status auf. Wenn ein Slice nicht hochgeladen werden kann, wird das Frontend benachrichtigt, das Slice erneut zu übertragen.

  • Wenn alle Slices erfolgreich hochgeladen wurden, liest der Backend-Dienst den Inhalt aller Slices und fügt sie zu einer vollständigen Datei zusammen. Das Zusammenführen von Dateien kann mit java.io.SequenceInputStream und BufferedOutputStream erreicht werden.

  • Zum Schluss geben Sie einfach das Antwortergebnis des erfolgreichen Datei-Uploads an das Frontend zurück.

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

Controller-Ebene:

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

Unter diesen ist file.upload-path der Speicherpfad für das Hochladen von Dateien, der in application.properties oder application.yml konfiguriert werden kann. Gleichzeitig müssen Sie eine RedisTemplate-Bean hinzufügen, um den Upload-Status aufzuzeichnen.

RedisTemplate-Konfiguration

Wenn Sie RedisTemplate verwenden müssen, müssen Sie das folgende Paket einführen:

Die Größe der hochgeladenen Slices muss jedes Mal kontrolliert werden, um die Upload-Geschwindigkeit und -Stabilität zu berücksichtigen, um zu vermeiden, dass zu viele Serverressourcen belegt werden oder es aufgrund von Netzwerkinstabilität zu Upload-Fehlern kommt.

    Es gibt eine Reihenfolge für das Hochladen von Slices. Sie müssen sicherstellen, dass alle Slices hochgeladen werden, bevor sie zusammengeführt werden. Andernfalls kann es zu unvollständigen Dateien oder Dateizusammenführungsfehlern kommen.
  • Nachdem der Upload abgeschlossen ist, müssen temporäre Dateien rechtzeitig bereinigt werden, um Serverabstürze zu vermeiden, die durch zu viel Speicherplatz verursacht werden. Sie können eine regelmäßige Aufgabe einrichten, um abgelaufene temporäre Dateien zu bereinigen.

Das obige ist der detaillierte Inhalt vonSo verwenden Sie Vue + Springboot zum Hochladen großer Dateien. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen