最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)目依賴、配置類實(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)目依賴

pom.xml 中添加必要依賴:

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

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

2.1 配置類

@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)文章

  • idea自定義TODO和快捷鍵設(shè)置方式

    idea自定義TODO和快捷鍵設(shè)置方式

    文章主要介紹了在setting中設(shè)置TODO Patterns和Filters以實(shí)現(xiàn)自定義TODO的快捷輸入,并詳細(xì)說(shuō)明了設(shè)置LiveTemplates以使自定義TODO與默認(rèn)TODO一致的過(guò)程
    2026-05-05
  • Java基于面向?qū)ο髮?shí)現(xiàn)一個(gè)戰(zhàn)士小游戲

    Java基于面向?qū)ο髮?shí)現(xiàn)一個(gè)戰(zhàn)士小游戲

    這篇文章主要為大家詳細(xì)介紹了Java如何基于面向?qū)ο髮?shí)現(xiàn)一個(gè)戰(zhàn)士小游戲,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以動(dòng)手嘗試一下
    2022-07-07
  • mybatis執(zhí)行錯(cuò)誤但sql執(zhí)行正常問(wèn)題

    mybatis執(zhí)行錯(cuò)誤但sql執(zhí)行正常問(wèn)題

    這篇文章主要介紹了mybatis執(zhí)行錯(cuò)誤但sql執(zhí)行正常問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 基于java時(shí)區(qū)轉(zhuǎn)換夏令時(shí)的問(wèn)題及解決方法

    基于java時(shí)區(qū)轉(zhuǎn)換夏令時(shí)的問(wèn)題及解決方法

    下面小編就為大家分享一篇基于java時(shí)區(qū)轉(zhuǎn)換夏令時(shí)的問(wèn)題及解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • 如何把第三方服務(wù)注冊(cè)到spring項(xiàng)目容器中

    如何把第三方服務(wù)注冊(cè)到spring項(xiàng)目容器中

    這篇文章主要為大家介紹了如何把第三方服務(wù)注冊(cè)到spring項(xiàng)目容器中,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • response文件流輸出文件名中文不顯示的解決

    response文件流輸出文件名中文不顯示的解決

    這篇文章主要介紹了response文件流輸出文件名中文不顯示的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • springboot配置嵌入式servlet容器的方法

    springboot配置嵌入式servlet容器的方法

    這篇文章主要介紹了springboot配置嵌入式servlet容器的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • 使用maven基本命令,打包包名問(wèn)題

    使用maven基本命令,打包包名問(wèn)題

    這篇文章主要介紹了使用maven基本命令,打包包名問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • SpringBoot如何查看和修改依賴的版本

    SpringBoot如何查看和修改依賴的版本

    這篇文章主要介紹了SpringBoot如何查看和修改依賴的版本問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Spring?Cache?集成?Caffeine實(shí)現(xiàn)項(xiàng)目緩存的示例

    Spring?Cache?集成?Caffeine實(shí)現(xiàn)項(xiàng)目緩存的示例

    本文主要介紹了Spring?Cache?集成?Caffeine實(shí)現(xiàn)項(xiàng)目緩存的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12

最新評(píng)論

伊金霍洛旗| 金寨县| 布尔津县| 合阳县| 乐都县| 阿瓦提县| 台湾省| 芮城县| 鄢陵县| 横峰县| 荥阳市| 原平市| 宁河县| 凤庆县| 苏尼特右旗| 东阳市| 无极县| 丽水市| 城步| 重庆市| 蓝山县| 临沧市| 北辰区| 凤山县| 无为县| 榕江县| 榆树市| 葵青区| 田东县| 色达县| 澜沧| 晋中市| 聊城市| 秦皇岛市| 通海县| 苏尼特右旗| 准格尔旗| 阳谷县| 洪洞县| 蒙城县| 彭阳县|