Jika anda perlu memuat naik fail besar, anda harus mempertimbangkan logik berikut:
Memuat naik fail besar secara amnya memerlukan muat naik fail dalam ketulan, dan kemudian memuat naik semua Slices digabungkan menjadi fail yang lengkap. Ia boleh dilaksanakan mengikut logik berikut:
Halaman hadapan memilih fail untuk dimuat naik pada halaman dan menggunakan kaedah Blob.slice untuk menghiris fail saiz setiap keping ialah nilai tetap (seperti 5MB) dan rekod jumlah keping yang ada.
Muat naik kepingan ke perkhidmatan backend secara berasingan, anda boleh menggunakan perpustakaan seperti XMLHttpRequest atau Axios untuk menghantar permintaan Ajax. Untuk setiap kepingan, tiga parameter perlu disertakan: indeks kepingan semasa (bermula dari 0), jumlah bilangan kepingan dan data fail kepingan.
Selepas perkhidmatan bahagian belakang menerima kepingan, ia menyimpannya ke fail sementara di bawah laluan yang ditentukan dan merekodkan indeks hirisan yang dimuat naik dan status muat naik. Jika hirisan gagal dimuat naik, bahagian hadapan dimaklumkan untuk menghantar semula hirisan.
Apabila semua kepingan berjaya dimuat naik, perkhidmatan hujung belakang membaca kandungan semua kepingan dan menggabungkannya ke dalam fail yang lengkap. Penggabungan fail boleh dicapai menggunakan java.io.SequenceInputStream dan BufferedOutputStream.
Akhir sekali, kembalikan hasil respons bagi muat naik fail yang berjaya ke bahagian hadapan.
<!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>
lapisan pengawal:
@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; }
Antaranya, file.upload-path ialah muat naik fail simpan Laluan boleh dikonfigurasikan dalam application.properties atau application.yml. Pada masa yang sama, anda perlu menambah Bean of RedisTemplate untuk merekodkan status muat naik.
Jika anda perlu menggunakan RedisTemplate, anda perlu memperkenalkan pakej berikut
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
Pada masa yang sama konfigurasikan maklumat redis dalam yml
spring.redis.host=localhost spring.redis.port=6379 spring.redis.database=0
dan kemudian dalam kelas anda sendiri Apabila menggunakan
@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); } }
Langkah Berjaga-jaga
Anda perlu mengawal saiz setiap kepingan yang dimuat naik untuk mengambil kira kelajuan dan kestabilan muat naik, dan elakkan mengambil terlalu banyak sumber pelayan atau masalah rangkaian Muat naik gagal kerana ketidakstabilan.
Terdapat urutan untuk memuat naik kepingan Anda perlu memastikan bahawa semua kepingan dimuat naik sebelum digabungkan Jika tidak, fail yang tidak lengkap atau ralat gabungan fail mungkin berlaku.
Selepas muat naik selesai, fail sementara perlu dibersihkan tepat pada masanya untuk mengelakkan ranap pelayan yang disebabkan oleh mengambil terlalu banyak ruang cakera. Anda boleh menyediakan tugas berkala untuk membersihkan fail sementara yang telah tamat tempoh.
Atas ialah kandungan terperinci Cara menggunakan vue+springboot untuk memuat naik fail besar. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!