文档的打开与下载

237 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

环境介绍

前后端分离,前端vue,后台springboot。

文件放置位置

位置1:

放在前端服务器,如/static/文件夹下。

(优缺点:方便管理,无需接口调用;但是安全性不高,直接输入文件位置即可访问;适用于安全性要求不高,无需动态修改文件内容的指定文件。)

位置2:

放在后台服务器,指定位置或者jar包中。

(优缺点:需要后台提供接口调用,安全性高,可校验用户权限后在确认打开或下载文件;适用于安全性有要求,需要动态修改文件内容的文件。)

打开还是下载

前端浏览器

通常情况下浏览器只支持.pdf格式,可以直接打开在线预览,再下载。

如果是.xls等格式,默认直接下载,且无法修改名字和路径。

后台接口调用

若调用后台接口打开.pdf格式文件,打开还是下载取决于返回消息头的设置。

// 直接下载文件

response.addHeader("Content-Disposition", "attachment;fileName=" + fileName); // fileName设置文件名

// 在浏览器端直接打开文件

response.addHeader("Content-Disposition", "inline;fileName=" + fileName); // fileName设置文件名

方法一(前端在线打开静态资源文件)

1、a标签,点击下载

<a href="/static/用户手册.pdf" target="_black">文档的打开与下载</a>

若有属性download="filename",强制下载,若无,根据target属性判断如何打开。

常用target属性如下:

  • target="_self",默认。在相同的框架中打开被链接文档。
  • target="_blank",在新窗口中打开被链接文档。

2、embed标签,嵌入页面

<embed src="/static/用户手册.pdf">

IE不支持,需要搭配object等一系列标签使用。

3、iframe标签,嵌入页面

<iframe src="/static/用户手册.pdf" width="100%" height="100%"></iframe>

IE不支持,非.pdf文件会提示下载或只读打开。

方法二(调用后台接口)

1、window.open接口直接下载

window.open(url + '?name=用户手册.pdf') // url为后台提供的接口

注意:window.open默认是get请求

2、获取文件流,前端处理下载

let url = this.$_apiUrl.curveHistoryReport
let params = {
  als: this.cycle,
  lts: this.dataTime[0],
  lte: this.dataTime[1],
  labels: labelArr
}
this.$axiosDownload(url, params, '历史曲线数据视图', '.xlsx')

export const axiosDownload = (url, params, fileName, fileType) => {
  return new Promise((resolve, reject) => {
    axios({
      method: 'post',
      url: url,
      data: params, // 注意post需要是data
      responseType: 'blob'
    }).then((res) => {
      if (!res) {
        resolve(0)
      } else {
        let disp = res.headers['content-disposition'].split(';')
        let filename = ''
        let filetype = ''
        // 后台返回的文件名和类型
        for (let i = 0; i < disp.length; i++) {
          if (disp[i].indexOf('filename=') !== -1) {
            let fileinfo = disp[i].replace('filename=', '')
            let filearr = fileinfo.split('.')
            filetype = '.' + filearr[filearr.length - 1]
            filename = fileinfo.replace(filetype, '')
            break
          }
        }
        // 如果前端传入了文件名\类型以前端为准
        if (fileName) filename = fileName
        if (fileType) filetype = fileType
        // 解析文件
        let blob = new Blob([res.data], {
          type: 'application/octet-stream'
        })
        let url = window.URL.createObjectURL(blob)
        let link = document.createElement('a')
        link.style.display = 'none'
        link.href = url
        link.setAttribute('download', decodeURI(filename + filetype)) // 文件名和后缀从后台获取
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        resolve(1)
      }
    }).catch(() => {
      resolve(0)
    })
  })
}

注意:

  • 若后台提供的接口类型是get,对应axios请求参数属性是params;
  • 若后台提供的接口类型是post,对应axios请求参数属性是data。

方法N(未尝试)

  • 1、pdf.js插件
  • 2、pdf2swf工具
  • 3、转换为SWF,使用FlexPa在线浏览

附打开下载关键代码

前端接口调用

1、获取文件blob大对象

// 获取blob大对象
export const axiosBlob = (url, params, callback, plus) => {
  return new Promise((resolve, reject) => {
    axios({
      method: 'get',
      url: url,
      params: params,
      responseType: 'arraybuffer'
    }).then((res) => {
      if (!res) {
        resolve(0)
      } else {
        let blob = new Blob([res.data], {
          type: 'application/octet-stream'
        })
        let fileReader = new FileReader()
        fileReader.readAsDataURL(blob) // readAsDataURL
        fileReader.onload = (e) => {
          if (typeof callback === 'function') { callFunction(callback, e.target.result, plus) }
        }
        fileReader.onerror = () => {
          reject(new Error('blobToBase64 error'))
        }
        resolve(1)
      }
    }).catch(() => {
      resolve(0)
    })
  })
}

2、获取blob的url

// 文件加载 提供在线打开 返回blob的url
export const axiosLoad = (url, params, callback) => {
  return new Promise((resolve, reject) => {
    axios({
      method: 'get',
      url: url,
      params: params, // get的数据是params接收 post的数据是data接收!!!
      responseType: 'blob'
    }).then((res) => {
      if (!res) {
        resolve(0)
      } else {
        let blob = new Blob([res.data], {
          type: 'application/octet-stream'
        })
        let loadUrl = window.URL.createObjectURL(blob)
        if (typeof callback === 'function') { callFunction(callback, loadUrl) }
        resolve(1)
      }
    }).catch(() => {
      resolve(0)
    })
  })
}

3、获取文档直接下载

// 文件下载
// resolve方法用于axiosDownload.then捕获
// reject方法用于axiosDownload.catch捕获
// 该处都使用resolve(e) 即调用axiosDownload完成后可以.then(e)进行异步处理
export const axiosDownload = (url, params, fileName, fileType) => {
  return new Promise((resolve, reject) => {
    axios({
      method: 'get',
      url: url,
      params: params,
      responseType: 'blob'
    }).then((res) => {
      if (!res) {
        resolve(0)
      } else {
        let disp = res.headers['content-disposition'].split(';')
        let filename = ''
        let filetype = ''
        // 后台返回的文件名和类型
        for (let i = 0; i < disp.length; i++) {
          if (disp[i].indexOf('filename=') !== -1) {
            let fileinfo = disp[i].replace('filename=', '')
            let filearr = fileinfo.split('.')
            filetype = '.' + filearr[filearr.length - 1]
            filename = fileinfo.replace(filetype, '')
            break
          }
        }
        // 如果前端传入了文件名\类型以前端为准
        if (fileName) filename = fileName
        if (fileType) filetype = fileType
        // 解析文件
        let blob = new Blob([res.data], {
          type: 'application/octet-stream'
        })
        let url = window.URL.createObjectURL(blob)
        let link = document.createElement('a')
        link.style.display = 'none'
        link.href = url
        link.setAttribute('download', decodeURI(filename + filetype)) // 文件名和后缀从后台获取
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        resolve(1)
      }
    }).catch(() => {
      resolve(0)
    })
  })
}

JAVA文档的打开与下载工具

public void fileDownloadByName(HttpServletResponse response,String name) throws Exception {
	String fileName = URLEncoder.encode(name, "utf-8");
	ClassPathResource resource = new ClassPathResource("/" + name);
	if (resource.getInputStream() != null) {
		fileUtil(resource.getInputStream(), response, fileName);
	}
}
public void fileUtil(InputStream fis,HttpServletResponse response,String fileName) {
	response.setContentType("application/force-download"); // 强制下载不打开
	response.addHeader("Content-Disposition", "attachment;fileName=" + fileName); // 设置文件名
	byte[] buffer = new byte[1024];
	BufferedInputStream bis = null;
	try {
		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);
		}
		log.debug("下载成功");
	} catch (Exception e) {
		log.error("下载失败:" + e);
	} finally {
		if (bis != null) {
			try {
				bis.close();
			} catch (IOException e) {
				log.error("下载失败:" + e);
			}
		}
		if (fis != null) {
			try {
				fis.close();
			} catch (IOException e) {
				log.error("下载失败:" + e);
			}
		}
	}
}