欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot集成Dufs通過(guò)WebDAV實(shí)現(xiàn)文件管理方式

 更新時(shí)間:2025年09月08日 08:45:09   投稿:jingxian  
文章介紹了在SpringBoot應(yīng)用中集成Dufs文件服務(wù)器的方法,使用WebDAV協(xié)議實(shí)現(xiàn)文件管理功能,具體步驟包括添加項(xiàng)目依賴(lài)、配置類(lèi)實(shí)現(xiàn)、服務(wù)層和控制器層的構(gòu)建,文章還詳細(xì)講解了大文件分塊上傳、異步操作和性能優(yōu)化措施

引言

在現(xiàn)代應(yīng)用開(kāi)發(fā)中,文件存儲(chǔ)和管理是一個(gè)常見(jiàn)需求。

Dufs 是一個(gè)輕量級(jí)的文件服務(wù)器,支持 WebDAV 協(xié)議,可以方便地集成到 Spring Boot 應(yīng)用中。

本文將詳細(xì)介紹如何使用 WebDAV 協(xié)議在 Spring Boot 中集成 Dufs 文件服務(wù)器。

1. 準(zhǔn)備工作

1.1 添加項(xiàng)目依賴(lài)

pom.xml 中添加必要依賴(lài):

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Sardine WebDAV 客戶(hù)端 -->
	<dependency>
	    <groupId>com.github.lookfirst</groupId>
	    <artifactId>sardine</artifactId>
	    <version>5.10</version>
	</dependency>
</dependencies>

2. 核心實(shí)現(xiàn)

2.1 配置類(lèi)

@Configuration
@ConfigurationProperties(prefix = "dufs.webdav")
@Data
public class DufsWebDavConfig {
    private String url = "http://localhost:5000";
    private String username = "admin";
    private String password = "password";
    private String basePath = "/";
    
    @Bean
    public Sardine sardine() {
        Sardine sardine = SardineFactory.begin();
        sardine.setCredentials(username, password);
        return sardine;
    }
}

2.2 服務(wù)層實(shí)現(xiàn)

@Service
@RequiredArgsConstructor
@Slf4j
public class DufsWebDavService {
    private final Sardine sardine;
    private final DufsWebDavConfig config;

    /**
     * 自定義 WebDAV 異常
     */
    public static class DufsWebDavException extends RuntimeException {
        public DufsWebDavException(String message) {
            super(message);
        }

        public DufsWebDavException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    /**
     * 構(gòu)建完整的 WebDAV 路徑
     */
    private String buildFullPath(String path) {
        return config.getUrl() + config.getBasePath() + (path.startsWith("/") ? path : "/" + path);
    }

    /**
     * 上傳文件
     */
    public String uploadFile(String path, InputStream inputStream) throws IOException {
        String fullPath = buildFullPath(path);
        sardine.put(fullPath, inputStream);
        return fullPath;
    }

    /**
     * 下載文件實(shí)現(xiàn)(方案1)
     * ByteArrayResource(適合小文件)
     */
    public Resource downloadFileByte(String path) {
        String fullPath = buildFullPath(path);
        try {
            InputStream is = sardine.get(fullPath);
            byte[] bytes = IOUtils.toByteArray(is); // 使用 Apache Commons IO
            return new ByteArrayResource(bytes) {
                @Override
                public String getFilename() {
                    return path.substring(path.lastIndexOf('/') + 1);
                }
            };
        } catch (IOException e) {
            throw new DufsWebDavException("Failed to download file: " + path, e);
        }
    }

    /**
     * 下載文件實(shí)現(xiàn)(方案2)
     * StreamingResponseBody(適合大文件)
     */
    public StreamingResponseBody downloadFileStreaming(String path) {
        String fullPath = buildFullPath(path);
        return outputStream -> {
            try (InputStream is = sardine.get(fullPath)) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }
        };
    }

    /**
     * 下載文件實(shí)現(xiàn)(方案3)
     * 自定義可重復(fù)讀取的 Resource(推薦)
     */
    public Resource downloadFile(String path) {
        String fullPath = buildFullPath(path);
        try {
            // 測(cè)試文件是否存在
            if (!sardine.exists(fullPath)) {
                throw new DufsWebDavException("File not found: " + path);
            }

            return new AbstractResource() {
                @Override
                public String getDescription() {
                    return "WebDAV resource [" + fullPath + "]";
                }

                @Override
                public InputStream getInputStream() throws IOException {
                    // 每次調(diào)用都獲取新的流
                    return sardine.get(fullPath);
                }

                @Override
                public String getFilename() {
                    return path.substring(path.lastIndexOf('/') + 1);
                }
            };
        } catch (IOException e) {
            throw new DufsWebDavException("Failed to download file: " + path, e);
        }
    }


    /**
     * 列出目錄下的文件信息
     */
    public List<WebDavFileInfo> listDirectory(String path) {
        String fullPath = buildFullPath(path);
        try {
            List<DavResource> resources = sardine.list(fullPath);
            return resources.stream().filter(res -> !res.getHref().toString().equals(fullPath + "/")).map(res -> new WebDavFileInfo(res.getHref().getPath(), res.isDirectory(), res.getContentLength(), res.getModified())).collect(Collectors.toList());
        } catch (IOException e) {
            throw new DufsWebDavException("Failed to list directory: " + path, e);
        }
    }

    /**
     * 創(chuàng)建目錄
     */
    public void createDirectory(String path) {
        String fullPath = buildFullPath(path);
        try {
            sardine.createDirectory(fullPath);
        } catch (IOException e) {
            throw new DufsWebDavException("Failed to create directory: " + path, e);
        }
    }


    /**
     * 刪除文件/目錄
     */
    public void delete(String path) {
        String fullPath = buildFullPath(path);
        try {
            sardine.delete(fullPath);
        } catch (IOException e) {
            throw new DufsWebDavException("Failed to delete: " + path, e);
        }
    }

    /**
     * 檢查文件/目錄是否存在
     */
    public boolean exists(String path) {
        String fullPath = buildFullPath(path);
        try {
            sardine.exists(fullPath);
            return true;
        } catch (IOException e) {
            return false;
        }
    }


    /**
     * 鎖定文件
     */
    public String lockFile(String path) {
        String fullPath = buildFullPath(path);
        try {
            return sardine.lock(fullPath);
        } catch (IOException e) {
            throw new DufsWebDavException("Failed to lock file: " + path, e);
        }
    }

    /**
     * 解鎖文件
     */
    public void unlockFile(String path, String lockToken) {
        String fullPath = buildFullPath(path);
        try {
            sardine.unlock(fullPath, lockToken);
        } catch (IOException e) {
            throw new DufsWebDavException("Failed to unlock file: " + path, e);
        }
    }

    /**
     * 文件信息DTO
     */
    @Data
    @AllArgsConstructor
    public static class WebDavFileInfo implements java.io.Serializable {
        private String name;
        private boolean directory;
        private Long size;
        private Date lastModified;
    }
}

2.3 控制器層

@RestController
@RequestMapping("/api/webdav")
@RequiredArgsConstructor
public class WebDavController {
    private final DufsWebDavService webDavService;

    @PostMapping("/upload")
    public ResponseEntity<?> uploadFile(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "path", defaultValue = "") String path) {
        try {
            String filePath = path.isEmpty() ? file.getOriginalFilename()
                    : path + "/" + file.getOriginalFilename();
            String uploadPath = webDavService.uploadFile(filePath, file.getInputStream());
            return ResponseEntity.ok().body(uploadPath);
        } catch (IOException e) {
            throw new DufsWebDavService.DufsWebDavException("File upload failed", e);
        }
    }

    @GetMapping("/download")
    public ResponseEntity<Resource> downloadFile(@RequestParam String path) {
        Resource resource = webDavService.downloadFileByte(path);
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    }

    @GetMapping("/downloadFileStreaming")
    public ResponseEntity<StreamingResponseBody> downloadFileStreaming(@RequestParam String path) {
        StreamingResponseBody responseBody = webDavService.downloadFileStreaming(path);
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + path + "\"")
                .body(responseBody);
    }

    @GetMapping("/list")
    public ResponseEntity<List<DufsWebDavService.WebDavFileInfo>> listDirectory(
            @RequestParam(required = false) String path) {
        return ResponseEntity.ok(webDavService.listDirectory(path == null ? "" : path));
    }

    @PostMapping("/directory")
    public ResponseEntity<?> createDirectory(@RequestParam String path) {
        webDavService.createDirectory(path);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

    @DeleteMapping
    public ResponseEntity<?> delete(@RequestParam String path) {
        webDavService.delete(path);
        return ResponseEntity.noContent().build();
    }

    @GetMapping("/exists")
    public ResponseEntity<Boolean> exists(@RequestParam String path) {
        return ResponseEntity.ok(webDavService.exists(path));
    }

3. 高級(jí)功能

3.1 大文件分塊上傳

public void chunkedUpload(String path, InputStream inputStream, long size) {
    String fullPath = buildFullPath(path);
    try {
        sardine.enableChunkedUpload();
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Length", String.valueOf(size));
        sardine.put(fullPath, inputStream, headers);
    } catch (IOException e) {
        throw new RuntimeException("Chunked upload failed", e);
    }
}

3.2 異步操作

@Async
public CompletableFuture<String> asyncUpload(String path, MultipartFile file) {
    try {
        uploadFile(path, file.getInputStream());
        return CompletableFuture.completedFuture("Upload success");
    } catch (IOException e) {
        CompletableFuture<String> future = new CompletableFuture<>();
        future.completeExceptionally(e);
        return future;
    }
}

4. 性能優(yōu)化

連接池配置

@Bean
public Sardine sardine() {
    Sardine sardine = SardineFactory.begin(username, password);
    sardine.setConnectionTimeout(5000);
    sardine.setReadTimeout(10000);
    return sardine;
}

流式下載大文件

@GetMapping("/download-large")
public ResponseEntity<StreamingResponseBody> downloadLargeFile(@RequestParam String path) {
    return ResponseEntity.ok()
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + path + "\"")
        .body(outputStream -> {
            try (InputStream is = sardine.get(buildFullPath(path))) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }
        });
}

5. 常見(jiàn)問(wèn)題解決

5.1 InputStream 重復(fù)讀取問(wèn)題

使用 ByteArrayResource 或緩存文件內(nèi)容解決:

public Resource downloadFile(String path) {
    return new ByteArrayResource(getFileBytes(path)) {
        @Override
        public String getFilename() {
            return path.substring(path.lastIndexOf('/') + 1);
        }
    };
}

5.2 異步方法返回錯(cuò)誤

Java 8 兼容的失敗 Future 創(chuàng)建方式:

@Async
public CompletableFuture<String> asyncOperation() {
    try {
        // 業(yè)務(wù)邏輯
        return CompletableFuture.completedFuture("success");
    } catch (Exception e) {
        CompletableFuture<String> future = new CompletableFuture<>();
        future.completeExceptionally(e);
        return future;
    }
}

結(jié)語(yǔ)

通過(guò)本文的介紹,我們實(shí)現(xiàn)了 Spring Boot 應(yīng)用與 Dufs 文件服務(wù)器通過(guò) WebDAV 協(xié)議的完整集成。

這種方案具有以下優(yōu)勢(shì):

  1. 輕量級(jí):Dufs 服務(wù)器非常輕量
  2. 功能全面:支持標(biāo)準(zhǔn) WebDAV 協(xié)議的所有操作
  3. 易于集成:Spring Boot 提供了良好的異步支持
  4. 性能良好:支持大文件流式傳輸

在實(shí)際項(xiàng)目中,可以根據(jù)需求進(jìn)一步擴(kuò)展功能,如添加文件預(yù)覽、權(quán)限控制等。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論