通过 a 标签实现预览和下载功能

在这里插入图片描述

download 属性定义了下载链接的地址。该属性需要下载资源是同源的。

  • 同源情况
    加上 download 属性后是下载功能
    不加则是预览功能
// 同源 此时是预览图片
<a target="_blank" href="/test.jpg">预览</a>
// 同源 此时是下载图片
<a target="_blank" href="/test.jpg" download>下载</a>

在 vue 中,通过代理后也可以实现
在这里插入图片描述

//  href="http://127.0.0.1:3000/uploads/20230731/4dbbe8b13342145d34e99ce00.webp.jpg"
// 此时用 /api 代替 http://127.0.0.1:3000 , 如下

<a href="/api/uploads/20230731/4dbbe8b13342145d34e99ce00.webp.jpg">预览</a>
<a href="/api/uploads/20230731/4dbbe8b13342145d34e99ce00.webp.jpg" download="">下载</a>
  • 不同源情况

浏览器默认都是预览

// 不同源 download 失效, 此时都是预览图片,而不是下载
<a href="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg">预览</a>
<a href="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg" download>预览</a>

不同源通过 href = src + '?response-content-type=application/octet-stream'; 实现强制下载

// 全是下载
<a href="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg?response-content-type=application/octet-stream">下载</a>
<a href="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg?response-content-type=application/octet-stream" download>下载</a>

通过 window.open 实现

window.open('https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg');
...

通过动态创建 a 标签实现

规则和上面一样

写法一
function downloadFile(src: string, fileName?: string) {
  const a = document.createElement('a');
  a.download = fileName ?? String(new Date().getTime());
  a.target = '_blank';
  a.href = src;
  a.click();
}

写法二
function downloadFile(src: string, fileName?: string) {
   const a = document.createElement('a'); 
   // 创建一个单击事件
   const event = new MouseEvent('click'); 
   // 设置文件名
   a.download = fileName ?? String(new Date().getTime());
   // 将生成的URL设置为a.href属性
   a.href = src; 
   // 触发a的单击事件
   a.dispatchEvent(event); 
}

通过 canvas 实现

function handleDownload(){
  // 下载图片地址和图片名
  const image = new Image();
  // 解决跨域 Canvas 污染问题
  image.setAttribute('crossOrigin', 'anonymous');
  image.onload = function () {
    const canvas = document.createElement('canvas');
    canvas.width = image.width;
    canvas.height = image.height;
    const context = canvas.getContext('2d');
    context?.drawImage(image, 0, 0, image.width, image.height);
    const url = canvas.toDataURL('image/png'); // 得到图片的base64编码数据
    downloadFile(url);
  };
  image.src = 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg';
}

通过 ajax 请求实现

function downloadFile(fileUrl,fileName){
    axios.get(fileUrl, { responseType: "blob" }).then(response => {
       const blob = new Blob([response.data]);
       const a = document.createElement("a");
       // 创建下载的链接
       a.href = window.URL.createObjectURL(blob);
       a.download = fileName;
       a.style.display = "none";
       //a标签追加元素到body内
       document.body.appendChild(a);
       //模拟点击下载
       a.click();
       // 下载完成移除元素
       document.body.removeChild(a);
       // 释放掉blob对象
       window.URL.revokeObjectURL(a.href);
    }).catch(console.error);
}

返回的格式
在这里插入图片描述

function handleDownload() {
	axios
		.get("http://127.0.0.1:3001/api/downloads", {
			responseType: "blob",
		})
		.then((res) => {
			console.log("res", res);
			downloadFile(URL.createObjectURL(res.data));
		});
}

// 也可以直接请求图片地址
function handleDownload2() {
	axios
		.get("http://127.0.0.1:3001/uploads/20230727/baca7d568e3cbbd1a0c360700.webp.jpg", {
			responseType: "blob",
		})
		.then((res) => {
			console.log("res", res);
			const blob = new Blob([res.data]);
			downloadFile(URL.createObjectURL(res.data));
		});
}

后端实现

所有情况通用的方式: 后端设置下载请求的响应头 Content-Disposition: attachment; filename=filename.jpg

  • inline 默认
  • attachment 表示让浏览器强制下载
  • filename 用于设置下载弹出框里预填的文件名

在这里插入图片描述

后端 express 为例。

const express = require("express");
const cors = require("cors");

const app = express();

app.use(cors());

app.get("/downloads", (req, res) => {
	res.setHeader("Content-Disposition", "attachment;filename=xxx.jpg");
	res.sendFile(__dirname + "/test.jpg");
});

app.listen(3000, () => {
	console.log("http://127.0.0.1:3000");
});

koa 为例

const send = require("koa-send");
const path = require("node:path");

router.get("/downloads", async (ctx, next) => {
	const filePath = "/uploads/20230727/baca7d568e3cbbd1a0c360700.webp.jpg";
	// ctx.attachment(filePath);
	// 响应文件格式
	await send(ctx, filePath, { root: path.join(__dirname, "../public") });
});

前端

// 此时由于后端控制也是强制下载
<a href="http://127.0.0.1:3001/api/downloads">下载</a>

(docx、excel、pdf) 文件的预览

vue-office
效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下载

#docx文档预览组件
npm install @vue-office/docx vue-demi@0.13.11

#excel文档预览组件
npm install @vue-office/excel vue-demi@0.13.11

#pdf文档预览组件
npm install @vue-office/pdf vue-demi@0.13.11

使用

<script lang="ts" setup>
	import { ref } from "vue";

	//引入VueOfficeDocx组件
	import VueOfficeDocx from "@vue-office/docx";
	//引入相关样式
	import "@vue-office/docx/lib/index.css";
	
	//引入VueOfficeExcel组件
	import VueOfficeExcel from "@vue-office/excel";
	//引入相关样式
	import "@vue-office/excel/lib/index.css";
	
	//引入VueOfficePdf组件
	import VueOfficePdf from "@vue-office/pdf";
	
	const docx = ref("");
	const excel = ref("");
	const pdf = ref("");
	
	function rendered() {
		console.log("渲染完成");
	}
	function errorHandler() {
		console.log("渲染失败");
	}
</script>

<template>
	<div>
		<button @click="docx = 'http://127.0.0.1:3000/uploads/20231213/c938a52037e05a351d4aaf300.docx'">预览 docx</button>
		<vue-office-docx v-if="docx" :src="docx" class="preview" style="height: 100vh" @rendered="rendered" />
	</div>
	
	<div>
		<button @click="excel = 'http://127.0.0.1:3000/uploads/20231213/c938a52037e05a351d4aaf301.xlsx'">
			预览 excel
		</button>
		<vue-office-excel v-if="excel" :src="excel" class="preview" style="height: 100vh" @rendered="rendered" />
	</div>
	
	<div>
		<button @click="pdf = 'http://static.shanhuxueyuan.com/test.pdf'">预览 pdf</button>
		<vue-office-pdf
			v-if="pdf"
			:src="pdf"
			class="preview"
			style="height: 100vh"
			@rendered="rendered"
			@error="errorHandler"
		/>
	</div>
</template>
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐