This is the sixth day of my participation in the August More text Challenge. For details, see:August is more challenging

Recently, the project needs to realize the requirement of downloading files by clicking the button. Vue is used in the front end, because files are of various types, such as pictures, PDF, Word and so on. Here the back end can return the address of the file to the front end, but I looked at the various answers on the Internet, and none of them seem to be what I want.

Since we are not sure what Type of file is, when we save the file to the database, we should save the content-type of the file together. In this way, when the file is retrieved from the database and returned to the front end, the front end can parse the file with the content-type identifier.

1. Back-end code

So I’m going to write the interface for the back end, and think about what you need on the back end. Since the file information is pre-stored in the database, we only need to pass in the primary key ID to get the file information. Once the parameters are determined, you need to determine the return value type. We can use ResponseEntity here. ResponseEntity can return more than one piece of information at a time, including status code, response header, response content, and so on.

Without further ado, look at the code.

/** * Download the attachment *@param attachmentId
 * @return* /
public ResponseEntity<byte[]> download(Long attachmentId) {
    // Check whether the attachment exists
    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();
        // Fetch the data from the stream
        int len = 0;
        byte[] buf = new byte[256];
        while ((len = ins.read(buf, 0.256> -))1) {
            bos.write(buf, 0, len);
        }

        // Prevent Garbled Chinese characters
        fileName = URLEncoder.encode(fileName, "utf-8");
        // Set the response header
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Disposition"."attachment; filename=" + fileName);
        headers.add("Content-Type", sysAttachment.getContentType());
        // Set the response
        HttpStatus statusCode = HttpStatus.OK;
        ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(bos.toByteArray(), headers, statusCode);
        return response;
    } catch (Exception e) {
        throw new CustomException("Download failed");
    } finally {
        try {
            if(ins ! =null) {
                ins.close();
            }
            if(bos ! =null) { bos.close(); }}catch (Exception e) {
            throw new CustomException("Download failed"); }}}Copy the code

Here we take out the url of the file from the database, and then get the input stream of the file through Aliyun OSS, and then output the file as binary, encapsulated in the ResponseEntity, and set the Type of the file to the content-type, in order to prevent the file name with Chinese garble, set the UTF-8 encoding, The back-end interface is complete.

When storing file information in the database, we should save at least the following fields: the URL of the file (usually given to you after uploading to OSS), the type of the file, the original file name, the file size, etc.

2. Front-end code

With the back-end interface in place, the front end comes next. Here you can encapsulate the file download method into a universal method and mount it globally, and then use it wherever you need it.

We need to identify the different files, so we need a key-value pair to represent the different files.

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'
}
Copy the code

Need to continue to add. The natural next step is to send the request, where the return type can be set to bloB and sent directly using Axios

/** * Download the attachment *@param Path Interface address *@param Param request parameter */
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)
  })
}
Copy the code

The interface address and request parameters are passed in externally. You must also carry the token, otherwise cross-domain access will occur. So once we get the data back from the back end, we’re going to parse the binary, so we’re going to define the resolveBlob method, which takes two arguments and returns the Type of the object and the file, and the Type of the file, which we already put in the content-type on the back end, so we’re going to just take it.

/** * Parse the blob response and download *@param {*} Res Blob Response content *@param {String} MimeType MIME type */
export function resolveBlob(res, mimeType) {
  const aLink = document.createElement('a')
  var blob = new Blob([res.data], { type: mimeType })
  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) // Set the name of the download file
  document.body.appendChild(aLink)
  aLink.click()
  document.body.removeChild(aLink);
}
Copy the code

This code does not need to be explained, the front end of the big guys naturally understand. OK, so at this point the back end code is done.

3, use,

It’s much easier to use. Mount to global first

import { downloadAttachment } from "@/utils/download"
Vue.prototype.downloadAttac = downloadAttachment
Copy the code

Call it directly where it is used

<el-button
    type="text"
    icon="el-icon-download"
    size="mini"
    @click="downloadAttachRow(scope.row.attachmentId)"
    ></el-button>

/** Download the attachment */
downloadAttachRow(attachId) {
    this.$confirm('Are you sure to download this file? '."Warning", {
        confirmButtonText: "Sure".cancelButtonText: "Cancel".type: "warning"
    }).then(() = > {
        this.downloadAttac('/system/attachment/download/', attachId)
    }).then(() = > {
        this.msgSuccess("Download successful")
    }).catch(() = >{})}Copy the code

That’s it. If you encounter any problems in the process, you can leave a comment below, I will reply.

Think good can help point a like ah, can also follow my public number [bald elder brother programming]