怎麼基於gitee實現上傳下載檔案的功能

2023-03-24 22:00:23

怎麼實現檔案的上傳下載功能?下面本篇文章給大家介紹一下基於gitee實現檔案上傳和下載功能的方法,希望對大家有所幫助!

方案的選擇

檔案的上傳和下載是我們這個專案的核心功能,也是整合優化了一下以前的boot專案來實現這個功能。

對於檔案的上傳和下載一般是使用阿里雲OSS、華為雲OSS這些,很好用而且官方提供了圖形介面,但是這些方式都需要按量儲存收費並且和gitee相似都是去呼叫官方介面實現功能,因為我是在學習階段,所以選擇了在gitee搭建了一個倉庫,利用官方的api向倉庫發起檔案的上傳、刪除功能,並且利用資料庫儲存的檔案地址實現將檔案下載到瀏覽器使用者端。

資料庫表的設計

資料庫的表暫時只用了兩張來實現基本功能,一個是檔案表,一個是資料夾表

public class File {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String fileName;
    private String filePath;
    private String fileSize;

    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    private Long userId;
    private Long folderId;
    //分享次數
    private Integer shareTimes;
    //檔案描述
    private String fileCover;
}
登入後複製
public class Folder {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String folderName;
    private Long fatherId;  //父資料夾id,為0表示沒有最上層資料夾
    private Long userId;
    private String folderCover;
    private Boolean folderPermissions;
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;
}
登入後複製

搭建gitee倉庫

首先開啟自己的gitee新建一個倉庫,填寫名稱,勾選初始化倉庫,建立好之後設定為開源

1.png

倉庫建立好之後開啟「個人主頁」—>「個人設定」---->「私人令牌」為自己生成一個新的私人令牌用於呼叫官方介面時的認證。

2.png

生成時不用管選項直接生成,注意儲存自己的令牌,因為只會展示一次。

gitee圖床工具類的編寫

這裡用的別人寫好的直接copy就行,注意裡面的私人令牌、個人空間、倉庫名、預設儲存地址要改成自己的,這個工具類也就是利用這些資訊通過HttpUtil工具類向gitee倉庫發起上傳請求。

package com.ityz.file.util;

import cn.hutool.core.codec.Base64;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @ClassName UploadGiteeImgBedUtil
 * @Author ityz
 * @Date 2022/11/23 16:38
 * @Description Gitee圖床工具類
 */
public class GiteeImgBedUtil {


    /**
     * 碼雲私人令牌
     */
    private static final String ACCESS_TOKEN = "0616f0e894e3c264bac45591e34a43bc";  //這裡不展示我自己的了,需要你自己補充


    /**
     * 碼雲個人空間名
     */
    private static final String OWNER = "procedure-yuan-yanzu";


    /**
     * 上傳指定倉庫
     */
    private static final String REPO = "files";


    /**
     * 預設上傳時指定存放圖片路徑
     */
    public static final String PATH = "files/";

    //API
    /**
     * 新建(POST)、獲取(GET)、刪除(DELETE)檔案:()中指的是使用對應的請求方式
     * %s =>倉庫所屬空間地址(企業、組織或個人的地址path)  (owner)
     * %s => 倉庫路徑(repo)
     * %s => 檔案的路徑(path)
     */
    private static final String API_CREATE_POST = "https://gitee.com/api/v5/repos/%s/%s/contents/%s";


    /**
     * 生成建立(獲取、刪除)的指定檔案路徑
     * @param originalFilename 原檔名
     * @param path 儲存檔案路徑
     * @return
     */
    private static String createUploadFileUrl(String originalFilename,String path){
        String targetPath = path == null ? GiteeImgBedUtil.PATH : path;
        //獲取檔案字尾
        String suffix = FileUtil.getFileSuffix(originalFilename);
        //拼接儲存的圖片名稱
        String fileName = System.currentTimeMillis()+"_"+ UUID.randomUUID().toString()+suffix;
        //填充請求路徑
        String url = String.format(GiteeImgBedUtil.API_CREATE_POST,
                GiteeImgBedUtil.OWNER,
                GiteeImgBedUtil.REPO,
                targetPath + fileName);
        return url;
    }

    private static String createDelFileUrl(String path){
        //填充請求路徑
        String url = String.format(GiteeImgBedUtil.API_CREATE_POST,
                GiteeImgBedUtil.OWNER,
                GiteeImgBedUtil.REPO,
                path);
        return url;
    }

    private static String createGetUrl(String path){
        String targetPath = path == null ? GiteeImgBedUtil.PATH : path;
        //填充請求路徑
        String url = String.format(GiteeImgBedUtil.API_CREATE_POST,
                GiteeImgBedUtil.OWNER,
                GiteeImgBedUtil.REPO,
                targetPath);
        return url;
    }

    /**
     * 獲取建立檔案的請求體map集合:access_token、message、content
     * @param multipartFile 檔案位元組陣列
     * @return 封裝成map的請求體集合
     */
    private static Map<String,Object> getUploadBodyMap(byte[] multipartFile){
        HashMap<String, Object> bodyMap = new HashMap<>(3);
        bodyMap.put("access_token", GiteeImgBedUtil.ACCESS_TOKEN);
        bodyMap.put("message", "add file!");
        bodyMap.put("content", Base64.encode(multipartFile));
        return bodyMap;
    }

    /**
     * 建立普通攜帶請求體集合內容
     * @param map 額外引數
     * @param message 請求資訊
     * @return
     */
    private static Map<String,Object> getCommonBodyMap(HashMap map, String message){
        HashMap<String, Object> bodyMap = new HashMap<>(2);
        bodyMap.put("access_token", GiteeImgBedUtil.ACCESS_TOKEN);
        bodyMap.put("message", message);
        if (map != null){
            bodyMap.putAll(map);
        }
        return bodyMap;
    }

    /**
     * **********封裝好的實際呼叫方法*******************
     */

    //超時
    private static int TIMEOUT = 10 * 1000;

    /**
     * 上傳檔案
     * @param filename 檔名稱
     * @param path 路徑
     * @param sha 必備引數from 獲取倉庫具體路徑下的內容
     * @return
     */
    public static String uploadFile(String path, String originalFilename, byte[] data){
        String targetURL = GiteeImgBedUtil.createUploadFileUrl(originalFilename,path);
        //請求體封裝
        Map<String, Object> uploadBodyMap = GiteeImgBedUtil.getUploadBodyMap(data);
        return HttpUtil.post(targetURL, uploadBodyMap);
    }


    /**
     * 刪除指定path路徑下的檔案
     * @param filename 檔名稱
     * @param path 路徑
     * @param sha 必備引數from 獲取倉庫具體路徑下的內容
     * @return
     */
    public static String deleteFile(String path,String sha){
        String delFileUrl = createDelFileUrl(path);
        HashMap<String, Object> needMap = new HashMap<>(1);
        needMap.put("sha",sha);//新增sha引數
        return HttpUtil.createRequest(Method.DELETE, delFileUrl)
                .form(getCommonBodyMap(needMap,"del file!"))  //構建請求表單
                .timeout(TIMEOUT)
                .execute().body();
    }

    /**
     * 獲取倉庫具體路徑下的內容,主要是獲取 sha
     * @param path
     * @return
     */
    public static String getSha(String path){
        String getShaUrl = createDelFileUrl(path);
        return HttpUtil.createRequest(Method.GET, getShaUrl)
                .form(getCommonBodyMap(null, "get sha!"))
                .timeout(TIMEOUT)
                .execute().body();
    }

}
登入後複製

檔案上傳介面

檔案上傳的過程為前端傳入檔案物件和相關資訊然後我們向gitee發起請求將檔案傳到倉庫,上傳成功之後在將返回的下載地址和相關資訊存入資料庫當中。

中間工具類

這裡我們先來編寫一箇中間工具類來發起請求和拿到結果,這個程式碼也寫在服務當中的話會顯得程式碼太長太複雜所以我單獨拿出來寫。

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ityz.common.constants.GiteeConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

/**
 * gitee檔案操作api
 */
@Slf4j
public class GiteeApi {
    /**
     * 上傳檔案api
     * @param multipartFile 前端傳入的檔案物件
     * @param folder 檔案所存資料夾名稱
     * @return gitee的api上傳返回值的json物件
     * @throws IOException
     */
    public static JSONObject upload(MultipartFile multipartFile,String folder) throws IOException {
        log.info("uploadFile()請求已來臨...");
        //根據檔名生成指定的請求url
        String originalFilename = multipartFile.getOriginalFilename();
        if (originalFilename == null) {
            log.info("伺服器接收檔案失敗!");
        }
        //Gitee請求:傳送上傳檔案請求
        String JSONResult = GiteeImgBedUtil.uploadFile(folder, originalFilename, multipartFile.getBytes());
        //解析響應JSON字串
        //上傳txt檔案時會出問題,沒解決
        JSONObject jsonObj = JSONUtil.parseObj(JSONResult);
        //請求失敗
        if (jsonObj.getObj(GiteeConstant.RESULT_BODY_COMMIT) == null) {
            log.info("上傳檔案失敗!");
        }
        //請求成功:返回下載地址
        JSONObject content = JSONUtil.parseObj(jsonObj.getObj(GiteeConstant.RESULT_BODY_CONTENT));
        log.info("上傳成功,下載地址為:" + content.getStr(GiteeConstant.RESULT_BODY_DOWNLOAD_URL));
        return content;
    }
}
登入後複製

Controller

controller拿到檔案和資訊後呼叫中間工具類上傳檔案,然後從請求頭中拿到使用者id,從返回物件中拿到檔案資訊存入File類當中然後使用mybatisplus將物件存入資料庫就行。

@RestController
@Slf4j
@RequestMapping("/file")
public class FileController {
    @Autowired
    private FileService fileService;
    @Autowired
    private HttpServletRequest request;

    /**
     * 檔案上傳介面
     * @param multipartFile 上傳的檔案物件
     * @param fileCover 檔案的描述資訊
     * @param folderId 檔案所屬的資料夾id
     * @return 使用者檔案地址
     * @throws IOException
     */
    @PostMapping("/upload")
    public Result<String> uploadFile(@RequestParam("file") MultipartFile multipartFile
            , @RequestParam("fileCover") String fileCover
            , @RequestParam("folderId") Long folderId) throws IOException {
        JSONObject content = GiteeApi.upload(multipartFile, "test/");
        //獲取userId
        Long userId = Long.valueOf(TokenUtil.parseToken(request.getHeader("token")));
        File file = new File();
        file.setFileCover(fileCover);
        file.setUserId(userId);
        file.setFolderId(folderId);
        file.setFileName(content.getStr(GiteeConstant.RESULT_BODY_NAME));;
        file.setFileSize(content.getStr(GiteeConstant.RESULT_BODY_SIZE));
        file.setFilePath(content.getStr(GiteeConstant.RESULT_BODY_DOWNLOAD_URL));
        fileService.fileUpload(file);
        return new Result<>(ResultCode.SUCCESS,ResultCode.UP_FILE_SUCCESS,content.getStr(GiteeConstant.RESULT_BODY_DOWNLOAD_URL));
    }
}
登入後複製

service比較簡單,設定一個儲存時間呼叫mapper存入就行

/**
     * 上傳檔案
     *
     * @param file 用於向資料庫儲存的檔案物件
     */
    @Override
    public void fileUpload(File file) {
        file.setCreateTime(new Date());
        fileMapper.insert(file);
    }
登入後複製

介面測試

我們隨便上傳一個檔案

3.png

檢視gitee和資料庫是否有相應結果

4.png

5.png

這裡id為2是因為之前刪除了一條資料,想要從頭開始截斷表就行。

檔案下載介面

檔案下載最初比較困擾我,有兩種方式,第一種是直接將檔案物件寫入到使用者提供的本地資料夾地址,第二種是將檔案下載到瀏覽器。最終選擇了第二種方式,因為使用者不用設定下載地址而且在瀏覽器下載可以看到下載過程,整個流程是利用下載地址將檔案轉換為檔案流寫入位元組陣列,在利用瀏覽器通過下載的方式寫出位元組陣列到輸出流。

controller

使用檔案id先從資料庫查到相應地址然後利用地址完成後續操作。

/**
     * 檔案下載介面
     * @param fileId 檔案id
     * @param response http響應物件
     * @return 下載結果
     */
    @GetMapping("/download")
    public Result<String> download(Long fileId,HttpServletResponse response) {
        try {
            String downloadUrl = fileService.downloadFile(fileId);
            URL url = new URL(downloadUrl);
            URLConnection conn = url.openConnection();
            InputStream bis = conn.getInputStream();
            byte[] bytes = new byte[bis.available()];
            OutputStream os = response.getOutputStream();
            // 從檔案流讀取位元組到位元組陣列中
            while (bis.read(bytes) != -1) {
                // 重置 response
                response.reset();
                // 設定 response 的下載響應頭
                response.setContentType("application/x-download");
                response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(FileUtil.getFileName(downloadUrl), "UTF-8"));  // 注意,這裡要設定檔名的編碼,否則中文的檔名下載後不顯示
                // 寫出位元組陣列到輸出流
                os.write(bytes);
                // 重新整理輸出流
                os.flush();
            }
            return new Result<>(ResultCode.SUCCESS,ResultCode.DOWN_FILE_SUCCESS);
        }catch (Exception e){
            e.printStackTrace();
            return new Result<>(ResultCode.FAIL,ResultCode.DOWN_FILE_FAIL);
        }
    }
登入後複製

這裡需要一個編寫獲取檔名的工具類,不然下載之後瀏覽器不知道檔案型別

/**
     * 獲取url中的檔名(取到最後一個/後面的就是檔名)
     * @param url 檔案地址
     * @return
     */
    public static String getFileName(String url) {
        if(!url.equals("")){
        return url.substring(url.lastIndexOf("/")+1);
        }
        else return "檔案地址錯誤";
    }
登入後複製

介面測試

傳送請求完成下載

6.png

檔案刪除介面

中間工具類

/**
     * 刪除檔案api
     * @param url 檔案地址
     */
    public static void del(String url){
        if (!url.equals("") && !url.contains("master/")) {
            log.info("url:" + url + " 無法解析路徑!");
        }
        String path = url.substring(url.indexOf("master/") + 7);
        log.info("解析取得待刪除路徑:" + path);
        String shaResult = GiteeImgBedUtil.getSha(path);
        JSONObject jsonObj = JSONUtil.parseObj(shaResult);
        String sha = jsonObj.getStr(GiteeConstant.RESULT_BODY_SHA);
        //3、Gitee請求:傳送刪除請求
        String JSONResult = GiteeImgBedUtil.deleteFile(path, sha);
        jsonObj = JSONUtil.parseObj(JSONResult);
        if (jsonObj.getObj(GiteeConstant.RESULT_BODY_COMMIT) == null) {
            log.info("刪除檔案失敗!");
        }
        log.info("檔案路徑為:" + path + " 刪除成功!");
    }
登入後複製

service

獲取檔案地址並刪除資料庫資料

/**
     * 刪除檔案
     * @param fileId 檔案id
     * @return
     */
    @Override
    public String delFile(Long fileId) {
        String url = fileMapper.selectById(fileId).getFilePath();
        fileMapper.deleteById(fileId);
        return url;
    }
登入後複製

controller

**
     * 刪除檔案介面
     * @param fileId 前端傳入的檔案id
     * @return
     */
    @GetMapping("/del")
    public Result<String> delFile(Long fileId) {
        try {
            String url = fileService.delFile(fileId);
            GiteeApi.del(url);
            return new Result<>(ResultCode.SUCCESS,ResultCode.DEL_FILE_SUCCESS);
        }
        catch (Exception e){
            e.printStackTrace();
            return new Result<>(ResultCode.FAIL,ResultCode.DEL_FILE_FAIL);
        }
    }
登入後複製

介面測試

傳送請求刪除檔案

7.png

檢視資料庫和gitee倉庫

8.png

9.png

自此完成了檔案的上傳和下載功能,大家有需要的可以參考。

(學習視訊分享:)

以上就是怎麼基於gitee實現上傳下載檔案的功能的詳細內容,更多請關注TW511.COM其它相關文章!