JVM · Java虚拟机原理 · JVM上语言·框架· 生态系统Docker · Spring Boot · Kotlin · 微服务SpringBoot极简教程 · Spring Boot

Spring Boot之框架应用:个人云盘(一)

2020-11-22  本文已影响0人  狄仁杰666

前言

来啦老铁!

笔者学习Spring Boot有一段时间了,附上Spring Boot系列学习文章,欢迎取阅、赐教:

  1. 5分钟入手Spring Boot;
  2. Spring Boot数据库交互之Spring Data JPA;
  3. Spring Boot数据库交互之Mybatis;
  4. Spring Boot视图技术;
  5. Spring Boot之整合Swagger;
  6. Spring Boot之junit单元测试踩坑;
  7. 如何在Spring Boot中使用TestNG;
  8. Spring Boot之整合logback日志;
  9. Spring Boot之整合Spring Batch:批处理与任务调度;
  10. Spring Boot之整合Spring Security: 访问认证;
  11. Spring Boot之整合Spring Security: 授权管理;
  12. Spring Boot之多数据库源:极简方案;
  13. Spring Boot之使用MongoDB数据库源;
  14. Spring Boot之多线程、异步:@Async;
  15. Spring Boot之前后端分离(一):Vue前端;
  16. Spring Boot之前后端分离(二):后端、前后端集成;
  17. Spring Boot之前后端分离(三):登录、登出、页面认证;
  18. Spring Boot之面向切面编程:Spring AOP;
  19. Spring Boot之集成Redis(一):Redis初入门;
  20. Spring Boot之集成Redis(二):集成Redis;
  21. Spring Boot之集成Redis(三):Spring Cache + Redis;
  22. Spring Boot之文件上传、下载;
  23. Spring Boot中文乱码问题解决方案:亲测有效;

在之前的文章《Spring Boot之文件上传、下载》中,我们一起学会了如何使用Spring Boot制作文件服务器,轻松实现了文件的传输(上传、下载)。今天我打算将其扩展一下,我们一起来实现一个简易的:个人云盘

实现之前,我们先找一个对标的产品,比如:百度网盘

百度网盘

百度网盘的整体功能还是比较简单的,主要有几部分:

  1. 文件类型分类模块;
  2. 文件、文件夹列表模块;
  3. 文件、文件夹操作模块;
  4. 鉴权、用户中心模块;
  5. 文件搜索、展示模块;
  6. 云盘容量管理模块;
  7. 引流模块;

百度网盘因为涉及到商业化,所以需要一些商业化功能,而我们做的功能则不涉及到这个,咱们就做个精简版的,自己私底下用用就行了,不是咱不想做,是咱也做不到商业化啊!

在我的印象中网盘、云盘基本都是在亏钱的,这里可以聊一下我经历过的一个故事:

4年前在厦门某公司的时候,当时云盘正火,我所在的公司就做了一个云盘APP,提供给用户免费存放图片、视频,刚开始希望很美好,可是上线了之后,现实很骨感,我们发现用户数据增长太快,文件服务器很快就撑不住了,同时也没有办法快速变现(用户可选择的免费云盘产品太多了),印象中不到一个月,该app就下线了~

因此,我们剔除那些花里胡哨的,做点简单的:

  1. 文件、文件夹列表模块;
  2. 简易文件、文件夹操作模块;
  3. 简易用户鉴权模块;
  4. 简易容量管理模块;
  5. 简易文件搜索模块;

截至目前的代码已上传Git Hub仓库,欢迎取阅:

前后端代码隔离

整体步骤

  1. 技术栈阐述;
  2. 前端设计;
  3. 网站鉴权功能;
  4. 后端文件夹相关功能API;
  5. 后端文件相关功能API;
  6. 后端容量信息获取功能;
  7. 前后端集成及演示;

1. 技术栈阐述;

2. 前端设计;

(目前同时运行前后端代码可以登录、查看主页面)。

登录页面 云盘主页面

3. 网站鉴权功能;

整个网站代码基础来自于之前的实践,网站鉴权更是直接拿来使用,只是我把所有人的初始密码设置为:123。

接下来我先在后端粗略编写一些API,这些API还未与前端集成,到集成阶段也许需要调整、增减哦~

4. 后端文件夹相关功能API;

package com.github.dylanz666.controller;

import com.alibaba.fastjson.JSONArray;
import com.github.dylanz666.constant.APIStatus;
import com.github.dylanz666.domain.FolderInformation;
import com.github.dylanz666.domain.FolderOperationResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.util.Arrays;
import java.util.Objects;

/**
 * @author : dylanz
 * @since : 11/15/2020
 */
@RestController
@RequestMapping("/api/folder")
public class FolderOperationController {
    @Value(value = "${file.root.dir}")
    private String rootDir;

    /**
     * 创建文件夹
     *
     * @param folderName:文件夹名
     */
    @PostMapping("")
    public FolderOperationResponse createFolder(@RequestParam String folderName) {
        FolderOperationResponse folderOperationResponse = new FolderOperationResponse();
        File newFolder = new File(rootDir, folderName);
        try {
            if (newFolder.exists()) {
                folderOperationResponse.setStatus(APIStatus.FAIL.toString());
                folderOperationResponse.setMessage("The target folder name is already exist.");
                folderOperationResponse.setFolderName(folderName);
                return folderOperationResponse;
            }
            newFolder.mkdir();
            folderOperationResponse.setStatus(APIStatus.SUCCESS.toString());
            folderOperationResponse.setMessage(APIStatus.SUCCESS.toString());
            folderOperationResponse.setFolderName(folderName);
        } catch (Exception e) {
            e.printStackTrace();
            folderOperationResponse.setStatus(APIStatus.FAIL.toString());
            folderOperationResponse.setMessage(e.getMessage());
            folderOperationResponse.setFolderName(folderName);
        }
        return folderOperationResponse;
    }

    /**
     * 删除文件夹
     *
     * @param folderName 文件夹名
     * @param all        是否递归删除文件夹下文件
     */
    @DeleteMapping("")
    public FolderOperationResponse deleteFolder(@RequestParam String folderName, @RequestParam String all) {
        FolderOperationResponse folderOperationResponse = new FolderOperationResponse();
        File folder = new File(rootDir, folderName);

        try {
            if (!folder.exists()) {
                folderOperationResponse.setStatus(APIStatus.FAIL.toString());
                folderOperationResponse.setMessage("The folder is not exist.");
                folderOperationResponse.setFolderName(folderName);
                return folderOperationResponse;
            }
            if (!folder.isDirectory()) {
                folderOperationResponse.setStatus(APIStatus.FAIL.toString());
                folderOperationResponse.setMessage("This is not a folder.");
                folderOperationResponse.setFolderName(folderName);
                return folderOperationResponse;
            }
            if (all.equalsIgnoreCase("Y") || all.equalsIgnoreCase("yes") || all.equalsIgnoreCase("true")) {
                deleteFolder(rootDir + "\\" + folderName);
                folderOperationResponse.setStatus(APIStatus.SUCCESS.toString());
                folderOperationResponse.setMessage(APIStatus.SUCCESS.toString());
                folderOperationResponse.setFolderName(folderName);
                return folderOperationResponse;
            }
            if (Objects.requireNonNull(folder.listFiles()).length > 0) {
                folderOperationResponse.setStatus(APIStatus.FAIL.toString());
                folderOperationResponse.setMessage("This is not a empty folder, if you still want to delete it, please pass a parameter: all, and it's value to be Y or Yes.");
                folderOperationResponse.setFolderName(folderName);
                return folderOperationResponse;
            }
            folder.delete();

            folderOperationResponse.setStatus(APIStatus.SUCCESS.toString());
            folderOperationResponse.setMessage(APIStatus.SUCCESS.toString());
            folderOperationResponse.setFolderName(folderName);
        } catch (Exception e) {
            e.printStackTrace();
            folderOperationResponse.setStatus(APIStatus.FAIL.toString());
            folderOperationResponse.setMessage(e.getMessage());
            folderOperationResponse.setFolderName(folderName);
        }
        return folderOperationResponse;
    }

    /**
     * 更新除文件夹名字
     *
     * @param folderInformation 文件夹信息
     */
    @PutMapping("")
    public FolderOperationResponse updateFolder(@RequestBody FolderInformation folderInformation) {
        FolderOperationResponse folderOperationResponse = new FolderOperationResponse();
        String currentFolderName = folderInformation.getCurrentFolderName();
        String targetFolderName = folderInformation.getTargetFolderName();

        File currentFolder = new File(rootDir, currentFolderName);
        File targetFolder = new File(rootDir, targetFolderName);
        try {
            if (targetFolder.exists()) {
                folderOperationResponse.setStatus(APIStatus.FAIL.toString());
                folderOperationResponse.setMessage("The target folder name is already exist.");
                folderOperationResponse.setFolderName(targetFolderName);
                return folderOperationResponse;
            }
            if (currentFolder.exists()) {
                currentFolder.renameTo(targetFolder);

                folderOperationResponse.setStatus(APIStatus.SUCCESS.toString());
                folderOperationResponse.setMessage(APIStatus.SUCCESS.toString());
                folderOperationResponse.setFolderName(targetFolderName);
                return folderOperationResponse;
            }
            folderOperationResponse.setStatus(APIStatus.FAIL.toString());
            folderOperationResponse.setMessage("The folder is not exist.");
            folderOperationResponse.setFolderName(currentFolderName);
        } catch (Exception e) {
            e.printStackTrace();
            folderOperationResponse.setStatus(APIStatus.FAIL.toString());
            folderOperationResponse.setMessage(e.getMessage());
            folderOperationResponse.setFolderName(currentFolderName);
        }
        return folderOperationResponse;
    }

    /**
     * 验证文件夹是否存在
     *
     * @param folderName 文件夹名
     */
    @GetMapping("")
    public FolderOperationResponse getFolder(@RequestParam String folderName) {
        FolderOperationResponse folderOperationResponse = new FolderOperationResponse();
        File folder = new File(rootDir, folderName);
        try {
            if (!folder.exists()) {
                folderOperationResponse.setStatus(APIStatus.FAIL.toString());
                folderOperationResponse.setMessage("The folder is not exist.");
                folderOperationResponse.setFolderName(folderName);
                return folderOperationResponse;
            }
            if (folder.isDirectory()) {
                folderOperationResponse.setStatus(APIStatus.FAIL.toString());
                folderOperationResponse.setMessage("This is not a folder.");
                folderOperationResponse.setFolderName(folderName);
                return folderOperationResponse;
            }
            folderOperationResponse.setStatus(APIStatus.SUCCESS.toString());
            folderOperationResponse.setMessage("The folder is exist.");
            folderOperationResponse.setFolderName(folderName);
        } catch (Exception e) {
            e.printStackTrace();
            folderOperationResponse.setStatus(APIStatus.FAIL.toString());
            folderOperationResponse.setMessage(e.getMessage());
            folderOperationResponse.setFolderName(folderName);
        }
        return folderOperationResponse;
    }

    /**
     * 获取文件夹下的内容
     *
     * @param folderName 文件夹名
     * @param all 是否包含所有内容,是则获取包含文件夹的所有内容,否则只获取文件
     */
    @GetMapping("/children")
    public FolderOperationResponse getFiles(@RequestParam String folderName, @RequestParam String all) {
        FolderOperationResponse folderOperationResponse = new FolderOperationResponse();
        try {
            File folder = new File(rootDir, folderName);
            if (folder.exists()) {
                String[] fileList = folder.list();
                JSONArray list = new JSONArray();
                assert fileList != null;
                if (all.equalsIgnoreCase("Y") || all.equalsIgnoreCase("yes") || all.equalsIgnoreCase("true")) {
                    list.addAll(Arrays.asList(fileList));

                    folderOperationResponse.setStatus(APIStatus.SUCCESS.toString());
                    folderOperationResponse.setMessage(APIStatus.SUCCESS.toString());
                    folderOperationResponse.setFolderName(folderName);
                    folderOperationResponse.setFiles(list);
                    return folderOperationResponse;
                }

                for (String item : fileList) {
                    File sub = new File(rootDir, folderName + "\\" + item);
                    if (sub.isDirectory()) {
                        list.add(item);
                    }
                }
                folderOperationResponse.setStatus(APIStatus.SUCCESS.toString());
                folderOperationResponse.setMessage(APIStatus.SUCCESS.toString());
                folderOperationResponse.setFolderName(folderName);
                folderOperationResponse.setFiles(list);
                return folderOperationResponse;
            }
            folderOperationResponse.setStatus(APIStatus.FAIL.toString());
            folderOperationResponse.setMessage("The folder is not exist.");
            folderOperationResponse.setFolderName(folderName);
            folderOperationResponse.setFiles(new JSONArray());
        } catch (Exception e) {
            e.printStackTrace();
            folderOperationResponse.setStatus(APIStatus.FAIL.toString());
            folderOperationResponse.setMessage(e.getMessage());
            folderOperationResponse.setFolderName(folderName);
            folderOperationResponse.setFiles(new JSONArray());
        }
        return folderOperationResponse;
    }

    private void deleteFolder(String path) {
        File folder = new File(path);
        if (!folder.isFile()) {
            File[] files = folder.listFiles();
            if (files != null) {
                for (File f : files) {
                    deleteFolder(f.getAbsolutePath());
                }
            }
        }
        folder.delete();
    }
}

5. 后端文件相关功能API;

package com.github.dylanz666.controller;

import com.github.dylanz666.constant.APIStatus;
import com.github.dylanz666.domain.FileInformation;
import com.github.dylanz666.domain.FileOperationResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Objects;

/**
 * @author : dylanz
 * @since : 11/02/2020
 */
@RestController
@RequestMapping("/api/file")
public class FileOperationController {
    @Value(value = "${file.root.dir}")
    private String rootDir;

    private FileInputStream fis = null;
    private BufferedInputStream bis = null;

    /**
     * 向系统增加文件(上传)
     *
     * @param folderName 上传目标子文件夹
     * @param file       上传的文件
     */
    @PostMapping("")
    public FileOperationResponse upload(@RequestParam String folderName, @RequestParam("file") MultipartFile file) {
        FileOperationResponse fileOperationMessage = new FileOperationResponse();
        String fileName = file.getOriginalFilename();
        assert fileName != null;
        try {
            File dest = new File(rootDir, folderName + "\\" + fileName);

            //当文件父文件夹不存在,则创建该文件夹
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdir();
            }

            file.transferTo(dest);

            fileOperationMessage.setStatus(APIStatus.SUCCESS.toString());
            fileOperationMessage.setMessage(APIStatus.SUCCESS.toString());
            fileOperationMessage.setFolderName(folderName);
            fileOperationMessage.setFileName(fileName);
        } catch (Exception e) {
            e.printStackTrace();
            fileOperationMessage.setStatus(APIStatus.FAIL.toString());
            fileOperationMessage.setMessage(e.getMessage());
            fileOperationMessage.setFolderName(folderName);
            fileOperationMessage.setFileName(fileName);
        }
        return fileOperationMessage;
    }

    /**
     * 批量向系统增加文件(上传)
     *
     * @param folderName 上传目标子文件夹
     * @param files      上传的文件们,调用API时,form-data中可传多个file名字的文件
     */
    @PostMapping("/batch")
    public FileOperationResponse uploadFiles(@RequestParam String folderName, @RequestParam("file") List<MultipartFile> files) {
        FileOperationResponse fileOperationMessage = new FileOperationResponse();

        StringBuilder responseFileName = new StringBuilder();
        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();
            assert fileName != null;
            responseFileName.append(fileName).append(";");
            try {
                File dest = new File(rootDir, folderName + "\\" + fileName);
                //当文件父文件夹不存在,则创建该文件夹
                if (!dest.getParentFile().exists()) {
                    dest.getParentFile().mkdir();
                }

                file.transferTo(dest);
            } catch (Exception e) {
                e.printStackTrace();
                fileOperationMessage.setStatus(APIStatus.FAIL.toString());
                fileOperationMessage.setMessage(e.getMessage());
                fileOperationMessage.setFolderName(folderName);
                fileOperationMessage.setFileName(responseFileName.toString());
            }
            fileOperationMessage.setStatus(APIStatus.SUCCESS.toString());
            fileOperationMessage.setMessage(APIStatus.SUCCESS.toString());
            fileOperationMessage.setFolderName(folderName);
            fileOperationMessage.setFileName(responseFileName.toString());
        }

        return fileOperationMessage;
    }

    /**
     * 删除系统中的文件
     *
     * @param folderName 要删除的文件的目标文件夹
     * @param fileName   要删除的文件
     */
    @DeleteMapping("")
    public FileOperationResponse delete(@RequestParam String folderName, @RequestParam String fileName) {
        FileOperationResponse fileOperationMessage = new FileOperationResponse();
        File file = new File(rootDir, folderName + "\\" + fileName);
        try {
            if (file.exists()) {
                file.delete();

                fileOperationMessage.setStatus(APIStatus.SUCCESS.toString());
                fileOperationMessage.setMessage(APIStatus.SUCCESS.toString());
                fileOperationMessage.setFolderName(folderName);
                fileOperationMessage.setFileName(fileName);
                return fileOperationMessage;
            }
            fileOperationMessage.setStatus(APIStatus.FAIL.toString());
            fileOperationMessage.setMessage("The file is not exist.");
            fileOperationMessage.setFileName(fileName);
        } catch (Exception e) {
            e.printStackTrace();
            fileOperationMessage.setStatus(APIStatus.FAIL.toString());
            fileOperationMessage.setMessage(e.getMessage());
            fileOperationMessage.setFolderName(folderName);
            fileOperationMessage.setFileName(fileName);
        }
        return fileOperationMessage;
    }

    /**
     * 修改系统中的文件名
     *
     * @param fileInformation 文件信息
     */
    @PutMapping("")
    public FileOperationResponse updateFile(@RequestBody FileInformation fileInformation) {
        FileOperationResponse fileOperationMessage = new FileOperationResponse();
        String folder = rootDir + "\\" + fileInformation.getFolderName();
        String currentFileName = fileInformation.getCurrentFileName();
        String targetFileName = fileInformation.getTargetFileName();

        File currentFile = new File(folder, currentFileName);
        File targetFile = new File(folder, targetFileName);
        try {
            if (targetFile.exists()) {
                fileOperationMessage.setStatus(APIStatus.FAIL.toString());
                fileOperationMessage.setMessage("The target file name is already exist.");
                fileOperationMessage.setFileName(targetFileName);
                return fileOperationMessage;
            }
            if (currentFile.exists()) {
                currentFile.renameTo(targetFile);

                fileOperationMessage.setStatus(APIStatus.SUCCESS.toString());
                fileOperationMessage.setMessage(APIStatus.SUCCESS.toString());
                fileOperationMessage.setFileName(targetFileName);
                return fileOperationMessage;
            }
            fileOperationMessage.setStatus(APIStatus.FAIL.toString());
            fileOperationMessage.setMessage("The file is not exist.");
            fileOperationMessage.setFileName(currentFileName);
        } catch (Exception e) {
            e.printStackTrace();
            fileOperationMessage.setStatus(APIStatus.FAIL.toString());
            fileOperationMessage.setMessage(e.getMessage());
            fileOperationMessage.setFileName(currentFileName);
        }
        return fileOperationMessage;
    }

    /**
     * 下载系统中的文件
     *
     * @param folderName 被下载的目标子文件夹
     * @param fileName   被下载的文件
     */
    @GetMapping("/download")
    public FileOperationResponse download(@RequestParam String folderName, @RequestParam String fileName) {
        FileOperationResponse fileOperationMessage = new FileOperationResponse();
        String folder = rootDir + "\\" + folderName;
        try {
            File file = new File(folder, fileName);

            HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
            assert response != null;
            response.setContentType("application/force-download");
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));

            if (file.exists()) {
                byte[] buffer = new byte[1024];
                fis = new FileInputStream(file);
                bis = new BufferedInputStream(fis);
                OutputStream os = response.getOutputStream();

                int i = bis.read(buffer);
                while (i != -1) {
                    os.write(buffer, 0, i);
                    i = bis.read(buffer);
                }

                fileOperationMessage.setStatus(APIStatus.SUCCESS.toString());
                fileOperationMessage.setMessage(APIStatus.SUCCESS.toString());
                fileOperationMessage.setFolderName(folderName);
                fileOperationMessage.setFileName(fileName);
                return fileOperationMessage;
            }
            fileOperationMessage.setStatus(APIStatus.FAIL.toString());
            fileOperationMessage.setMessage("The file is not exist.");
            fileOperationMessage.setFileName(fileName);
        } catch (Exception e) {
            e.printStackTrace();
            fileOperationMessage.setStatus(APIStatus.FAIL.toString());
            fileOperationMessage.setMessage(e.getMessage());
            fileOperationMessage.setFolderName(folderName);
            fileOperationMessage.setFileName(fileName);
        } finally {
            try {
                bis.close();
                fis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return fileOperationMessage;
    }

    /**
     * 验证文件是否存在
     *
     * @param folderName 文件的文件夹名
     * @param fileName 文件名
     */
    @GetMapping("")
    public FileOperationResponse getFile(@RequestParam String folderName, @RequestParam String fileName) {
        FileOperationResponse fileOperationMessage = new FileOperationResponse();
        String folder = rootDir + "\\" + folderName;
        try {
            File file = new File(folder, fileName);
            if (!file.exists()) {
                fileOperationMessage.setStatus(APIStatus.FAIL.toString());
                fileOperationMessage.setMessage("The file is not exist.");
                fileOperationMessage.setFolderName(folderName);
                fileOperationMessage.setFileName(fileName);
                return fileOperationMessage;
            }
            fileOperationMessage.setStatus(APIStatus.SUCCESS.toString());
            fileOperationMessage.setMessage("The file is exist.");
            fileOperationMessage.setFileName(fileName);
        } catch (Exception e) {
            e.printStackTrace();
            fileOperationMessage.setStatus(APIStatus.FAIL.toString());
            fileOperationMessage.setMessage(e.getMessage());
            fileOperationMessage.setFolderName(folderName);
            fileOperationMessage.setFileName(fileName);
        }
        return fileOperationMessage;
    }
}

6. 后端容量信息获取功能;

package com.github.dylanz666.controller;

import com.github.dylanz666.constant.APIStatus;
import com.github.dylanz666.domain.FolderOperationResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;

/**
 * @author : dylanz
 * @since : 11/22/2020
 */
@RestController
@RequestMapping("/api/space")
public class SpaceController {
    @Value(value = "${file.root.dir}")
    private String rootDir;

    /**
     * 获取文件系统容量信息
     */
    @GetMapping("")
    public FolderOperationResponse getSpaceInfo() {
        File folder = new File(rootDir);

        FolderOperationResponse folderOperationResponse = new FolderOperationResponse();
        try {
            long free = folder.getFreeSpace();
            long total = folder.getTotalSpace();
            String freeString = (free / 1024 / 1024 / 1024) + "G";
            String totalString = (total / 1024 / 1024 / 1024) + "G";

            String message = freeString + "/" + totalString;

            folderOperationResponse.setStatus(APIStatus.SUCCESS.toString());
            folderOperationResponse.setMessage(message);
            return folderOperationResponse;
        } catch (Exception e) {
            e.printStackTrace();
            folderOperationResponse.setStatus(APIStatus.FAIL.toString());
            folderOperationResponse.setMessage(e.getMessage());
        }
        return folderOperationResponse;
    }
}

7. 前后端集成及演示;

这一步我们留到下次再来完成吧,今天就到这啦!

今天的文章虽然没有完整的完成一个个人云盘应用,但您从文章中仍可以学到一些东西的,如:

1). Java中/Spring Boot中常用的文件夹操作代码;
2). Java中/Spring Boot中常用的文件操作代码;
3). 个人云盘应用的设计、实现思路;
4). Spring Boot基础对于实现具体应用的重要性;
等。

下个礼拜,我们不见不散!

如果本文对您有帮助,麻烦点赞、关注!

谢谢!

上一篇下一篇

猜你喜欢

热点阅读