天天看點

Vue實作點選按鈕進行檔案下載下傳(後端Java)

最近項目中需要實作點選按鈕下載下傳檔案的需求,前端用的vue,因為檔案是各種類型的,比如圖檔、pdf、word之類的。這裡後端是可以傳回檔案的位址給前端的,但我看了下網上各種五花八門的答案,感覺都不是我想要的。

因為不确定檔案是哪種類型的,是以我們在儲存檔案到資料庫的時候,應該把檔案的

Content-Type

一起存入,這樣從資料庫取出傳回前端的時候,帶上

Content-Type

辨別是哪種類型的檔案,前端解析即可。

1、後端代碼

這裡我先寫後端的接口,考慮一下後端需要什麼東西。因為檔案資訊已經提前存入資料庫,是以我們隻需要傳入主鍵id就可以拿到檔案的資訊。确定參數後,就需要确定一下傳回值類型。這裡可以使用

ResponseEntity

傳回。

ResponseEntity

可以一次傳回多個資訊,包括狀态碼,響應頭資訊,響應内容等。

話不多說,看代碼。

/**
 * 下載下傳附件
 * @param attachmentId
 * @return
 */
public ResponseEntity<byte[]> download(Long attachmentId) {
    // 查詢附件是否存在
    SysAttachment sysAttachment = sysAttachmentMapper.selectSysAttachmentById(attachmentId);
    if (StringUtils.isNull(sysAttachment)) {
        return null;
    }

    ByteArrayOutputStream bos = null;
    InputStream ins = null;
    try {
        String fileName = sysAttachment.getOrgFileName();
        String ossFileName = sysAttachment.getUrl();
        bos = new ByteArrayOutputStream();
        ins = OssUtils.getInstance().getObject(ossFileName).getObjectContent();
        // 取流中的資料
        int len = 0;
        byte[] buf = new byte[256];
        while ((len = ins.read(buf, 0, 256)) > -1) {
            bos.write(buf, 0, len);
        }

        // 防止中文亂碼
        fileName = URLEncoder.encode(fileName, "utf-8");
        // 設定響應頭
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Disposition", "attachment;filename=" + fileName);
        headers.add("Content-Type", sysAttachment.getContentType());
        // 設定響應嗎
        HttpStatus statusCode = HttpStatus.OK;
        ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(bos.toByteArray(), headers, statusCode);
        return response;
    } catch (Exception e) {
        throw new CustomException("下載下傳失敗");
    } finally {
        try {
            if (ins != null) {
                ins.close();
            }
            if (bos != null) {
                bos.close();
            }
        } catch (Exception e) {
            throw new CustomException("下載下傳失敗");
        }
    }
}
           

這裡我們從資料庫拿出檔案的url後,再通過阿裡雲oss拿到檔案的輸入流,接着把檔案輸出為二進制,封裝到

ResponseEntity

中,并把檔案的類型設定到

Content-Type

中,同時為了防止檔案名帶有中文名亂碼,設定

utf-8

編碼,至此後端接口完成。

通過上面的資訊,我們在資料庫儲存檔案資訊時,至少應該儲存下面幾個字段:檔案的url(一般在上傳到oss後會給你一個)、檔案的類型、原始檔案名、檔案大小等。

2、前端代碼

有了後端接口,接下來就是前端了。這裡可以把檔案下載下傳的方法封裝成一個通用方法全局挂載,之後需要使用的地方直接使用即可。

我們需要辨別不同的檔案,是以我們需要一個鍵值對表示不同的檔案。

const mimeMap = {
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  xls: 'application/vnd.ms-excel',
  zip: 'application/zip',
  jpg: 'image/jpg',
  jpeg: 'image/jpeg',
  png: 'image/png',
  doc: 'application/msword',
  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  ppt: 'application/vnd.ms-powerpoint',
  txt: 'text/plain',
  pdf: 'application/pdf'
}
           

有需要的可以繼續補充。接下來自然就是發請求了,這裡的傳回類型可以設定為

blob

,使用axios直接發送

/**
 * 下載下傳附件
 * @param path 接口位址
 * @param param  請求參數
 */
export function downloadAttachment(path, param) {
  var url = baseUrl + path + param
  axios({
    method: 'get',
    url: url,
    responseType: 'blob',
    headers: { 'Authorization': getToken() }
  }).then(res => {
    resolveBlob(res, res.data.type)
  })
}
           

接口位址和請求參數從外部傳入。同時需要攜帶token,不然會跨域通路。拿到後端傳回的資料後,需要解析二進制檔案,這裡定義

resolveBlob

方法,該方法有兩個參數,傳回對象和檔案的類型,檔案的類型,我們在後端已經放入

Content-Type

中了,這裡直接取。

/**
 * 解析blob響應内容并下載下傳
 * @param {*} res blob響應内容
 * @param {String} mimeType MIME類型
 */
export function resolveBlob(res, mimeType) {
  const aLink = document.createElement('a')
  var blob = new Blob([res.data], { type: mimeType })
  // 從response的headers中擷取filename, 後端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 設定的檔案名;
  var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
  var contentDisposition = decodeURI(res.headers['content-disposition'])
  var result = patt.exec(contentDisposition)
  var fileName = result[1]
  fileName = fileName.replace(/\"/g, '')
  aLink.href = URL.createObjectURL(blob)
  aLink.setAttribute('download', fileName) // 設定下載下傳檔案名稱
  document.body.appendChild(aLink)
  aLink.click()
  document.body.removeChild(aLink);
}
           

這代碼不用多解釋了吧,前端大佬們自然看得懂。OK了啊,到這裡前後端代碼都完成了。

3、使用

import { downloadAttachment } from "@/utils/download"
Vue.prototype.downloadAttac = downloadAttachment
           
<el-button
    type="text"
    icon="el-icon-download"
    size="mini"
    @click="downloadAttachRow(scope.row.attachmentId)"
    ></el-button>

/** 下載下傳附件 */
downloadAttachRow(attachId) {
    this.$confirm('是否确認下載下傳該檔案?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
    }).then(() => {
        this.downloadAttac('/system/attachment/download/', attachId)
    }).then(() => {
        this.msgSuccess("下載下傳成功")
    }).catch(() => {})
}
           
覺得好的可以幫忙點個贊啊,也可以關注我的公衆号【秃頭哥程式設計】