js实现PDF 预览和文件下载

news2024/11/25 4:38:32

在开发过程中要求对 PDF 类型的发票提供 预览下载 功能,PDF 类型文件的来源又包括 H5 移动端PC 端,而针对这两个不同端的处理会有些许不同,下文会有所提及。

针对 PDF 预览 的文章不在少数,但似乎都没有提及可能遇到的问题,或是提供对应的具体需求场景下如何选择,因此,本文的核心就是结合实际需求场景下,看看目前各种实现方案到底哪一个更适合,当然希望大家可以在评论区对文中的内容进行斧正,或是提供更优质的方案。

基本要求:

  • 支持 pdf 文件 内容的 完整预览
  • 多页 pdf 文件 支持 分页查看
  • PC 端移动端 都需支持 下载预览

产品要求:

  • PC 端 的预览要支持在 当前页 进行预览
  • pdf 文件 预览时的字体要 和 实际文件的 字体保证一致性

PDF 预览

先抛开上面的各种要求,咱们先总结下目前实现 PDF 预览的几种常用方式:

  • 借助各种类库,基于代码实现预览,如基于 pdfjs-dist 的包
  • 直接基于各个浏览器内置的 PDF 预览插件,如 <iframe src="xxx">、<embed src="xxx" >
  • 服务端将 PDF 文件转换成图片

接下来分别看看以上方案如何实现,以及是否符合上述提供的要求!

<embed> / <iframe> 实现预览

<embed> 标签

<embed> 元素 将外部内容嵌入文档中的指定位置,此内容由 外部应用程序其他交互式内容源(如 浏览器插件)提供。

说简单点,就是使用 <embed> 来展示的资源是完全交由它所在的环境提供的展示功能,即如果当前的应用环境支持这个资源的展示那么就可以正常展示,如果不支持那就无法展示。

使用起来也是非常简单:

<embed  type="application/pdf"  :src="pdfUrl"  width="800"  height="600" />

多数现代浏览器已经弃用并取消了对浏览器插件的支持,现在已经不建议使用 <embed> 标签,但可以使用 <img>、<iframe>、<video>、<audio> 等标签代替。

<iframe> 标签

基于 <iframe> 的方式和以上差不多,整体效果也一致,这里这就不在额外展示:

<iframe  :src="pdfUrl"  width="800"  height="600" />

值得注意的是,即便使用的是 <iframe> 但实际展开其内层结构后你会发现:

其内部还是 <embed> 标签?这是怎么回事,不是说最好不建议使用 <embed> 吗?

首先来在 caniuse 查看兼容情况,如下:

我们再找一个不支持 <embed> 的浏览器,比如 IE,来试试效果:

换成 <iframe> 试试,如下:

显然,<embed> 在不兼容的环境直接无法显示,而 <iframe> 是能够正常识别的,只不过 <iframe> 加载的资源无法被 IE 浏览器处理,即本质原因是 IE 浏览器根本就不支持对类似 PDF 等文件的预览,比如当尝试直接在地址栏中输入 http://127.0.0.1:3000/src/assets/2.pdf 时会得到:

因此,通常情况下当浏览器不支持内联 PDF 时,应该提供一个 PDF 的回退链接,即以下载的方式来实现,而这就是 pdfobject 做的事情,实际上它的源码内容比较简单,核心就是 PDFObject 会检测浏览器对内联/嵌入 PDF 的支持,如果支持嵌入,则嵌入 PDF,如果浏览器不支持嵌入,则不会嵌入 PDF,并提供一个指向 PDF 的回退链接,例如在 IE 中的表现:

事实上,这其实只是帮我们少写了一些兼容性的代码而已,也不一定符合大部分人的场景,在这里提到只是因为其与 <embed> 之间存在的联系。

vue3-pdfjs 实现预览

为什么不直接使用 pdfjs-dist?

pdf.js 几个明显的可吐槽的点:

  • 包名称不统一,npm 上的包名叫 pdfjs-dist,然而在 Readme 中自己又称其为 pdf.js
  • 没有清晰的文档作为指引,只能通过其仓库中的 examples 目录的内容作为参考
  • 官方示例不够友好,例如没有提供 vue/react 等相关的示例
  • 直接使用需要引入很多文档没有指明的内容
  • 有时展示的 pdf 内容文字模糊或缺少部分等

因此,既然已经有基于 vue/react 封装好的包,这里就直接用来作为演示。

具体使用

安装和使用过程可参考 vue3-pdfjs ,具体 Vue3 示例代码如下:

js

复制代码

<script setup lang="ts"> 
import { onMounted, ref } from 'vue' 
import { VuePdf, createLoadingTask } from 'vue3-pdfjs/esm' 
import type { VuePdfPropsType } from 'vue3-pdfjs/components/vue-pdf/vue-pdf-props' 
// Prop type definitions can also be imported 
import type { PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api'
import pdfUrl from './assets/You-Dont-Know-JS.pdf'  
const pdfSrc = ref<VuePdfPropsType['src']>(pdfUrl) 
const numOfPages = ref(0) onMounted(() => {   
const loadingTask = createLoadingTask(pdfSrc.value)   
loadingTask.promise.then((pdf: PDFDocumentProxy) => {    
 numOfPages.value = pdf.numPages   }) }) </script> 
 
<template>
 <VuePdf v-for="page in numOfPages" :key="page" :src="pdfSrc" :page="page" /> 
</template> 
 
<style> @import '@/assets/base.css'; 
</style>

效果如下:

存在问题

看上去加载正常的 pdf 文档 似乎没啥大问题,来试试加载 pdf 发票 看看,但由于实际发票敏感信息较多,这里就不贴出原本的发票内容,直接来看预览后的发票内容:

  • 显然整体发票的 内容缺失得非常多,虽然某些发票大部分能够展示,但如 发票抬头印章 部分可能无法正常显示等

注意】无法显示完整的内容是因为 pdf.js 是需要一些字体库的支持,如果 原 PDF 文件 中部分字体没有匹配到字体库将无法在 pdf.js 中显示,而字体库存放在 cmaps 文件夹下 image.png

  • 另外,预览的字体实际的字体不一致 的,而由于发票的特殊性,对字体的一致性是有较大的要求,毕竟如果同一张发票字体不一致会缺乏 规范性 和 合法性(`被要求字体一致时的说法`

常见的解决方案: 解决 pdf.js 无法完全显示 pdf 文件内容的问题,实际上还是根据执行环境的错误信息进行分析,需要强行修改源码内容。

Mozilla Firefox(火狐浏览器)

Mozilla Firefox 内置的 PDF 阅读器实际就是 pdf.js,你可以直接用火狐浏览器预览一下 pdf 文件,如下:

并且大多基于 pdf.js 二次封装的库 vue-pdf、vue3-pdfjs 等在预览 pdf 文件的发票时通常无法显示完整内容,需要或多或少的涉及对源码的更改,而在 Firefox 中内置的 pdf.js 却能够完整的显示对应的 pdf 文件的内容。

PDF图片 实现预览

这种方式应该不用多说了,核心是服务端在响应 pdf 文件时,先转换成图片类型再返回,前端直接展示具体图片内容即可。

具体实现

下面通过用 node 来模拟:

const pdf = require('pdf-poppler') 
const path = require('path') 
const Koa = require('koa') 
const koaStatic = require('koa-static') 
const cors = require('koa-cors') 
const app = new Koa() 
// 跨域 
app.use(cors()) 
// 静态资源 
app.use(koaStatic('./server')) 
function getFileName(filePath) {   
  return filePath.split('/').pop().replace(/\.[^/.]+$/, '') 
} 
function pdf2png(filePath) {   
// 获取文件名   
const fileName = getFileName(filePath);   
const dir = path.dirname(filePath);   
// 配置参数   
const options = {     
format: 'png',     
out_dir: dir,     
out_prefix: fileName,     
page: null,   
}   
// pdf 转换 png   
return pdf.convert(filePath, options).then((res) => {
  console.log('Successfully converted !')       
  return `http://127.0.0.1:4000${dir.replace('./server','')}/${fileName}-1.png`     }).catch((error) => {              console.error(error)     }) } 
  // 响应 
  app.use(async (ctx) => {     
  if(ctx.path.endsWith('/getPdf')){         
  const url = await pdf2png('./server/pdf/2.pdf')         
  ctx.body = { url }     
  }else{
  ctx.body = 'hello world!'     } }) 
  app.listen(4000)

避免踩一些坑

坑一:不推荐 pdf-image

在实现服务端将 pdf 文件转换成图片时需要依赖到一些第三方包,一开始使用了 pdf-image 这个包,但在实际转换时发生较多的异常错误,顺着错误查看源码后发现其内部需要依赖一些额外的工具,因为其中需要使用 pdfinfo xxx 相关命令,并且其对应的 issue 上也存在着一些类似问题,但都试了试最后还是没有成功!

因此,更推荐使用 pdf-poppler 其中附带了一个 pdftocairo 的程序可以实现 pdf 到 图片 的转换能力,不过它目前版本支持 WindowsMac OS,如下:

坑二:path.basename not a function

在上述的代码内容中需要获取文件的名称,实际上我们可以简单直接的使用 Node Apipath.basename(path[, suffix]) 来达到目的:

但是在程序运行时发生了如下 异常,对应的 代码内容 和 运行结果 如下:

js

复制代码

// 配置参数 const options = { format: 'png', out_dir: dir, out_prefix: path.baseName(filePath, path.extname(filePath)), // 发生异常 page: null, }

这个暂时没有找到是什么原因,只能自己简单实现了一个 getFileName 方法用于获取文件的名称。

报错原因:太依赖编辑器的自动提示,将 basename 输出成 baseName ,没错就是 n 和 N 的区别.

坑三:细节

上述内容通过 koa 启动模拟业务服务,由于 业务服务(http://127.0.0.1:4000应用服务 (http://127.0.0.1:3000) 间的端口不一致,因此会产生 跨域,此时可以通过 koa-cors 来解决,值得注意的是有时候的那个业务服务器重启时 koa-cors 可能不起作用。

由于响应的内容直接在 koa 通用中间件中返回,因此,如果你需要支持业务服务提供 静态资源 的访问能力,就可以通过 koa-static 来实现,值得注意的是,当你通过 koa-static 指定静态文件资源后,如 app.use(koaStatic('./static')),此时如果你直接通过 http://127.0.0.1:4000/static/pdf/xxx.png 时,那么会得到 404 Not Found 的错误,原因在于 koa-static 是直接把 /static/ 设置成了 根路径,因此正确的访问路径为:http://127.0.0.1:4000/pdf/xxx.png

效果演示

发票内容不方便展示这里就不直接展示了,只需要关注生成的图片和路径即可:

PDF 下载

这里的下载实际不仅指 pdf 的下载,而是客户端方面所能支持的下载方式,最常见的如下几种:

  • a 标签,例如 <a href="xxxx" download="xxx">下载</a>
  • location.href,例如 window.location.href = xxx
  • window.open,例如 window.open(xxx)
  • Content-disposition,例如 Content-disposition:attachment;filename="xxx"

<a> 实现下载

<a>download 属性用于指示浏览器 下载 href 指定的 URL,而不是导航到该资源,通常会提示用户将其保存为本地文件,如果 download 属性有指定内容,这个值就会在下载保存过程中作为 预填充的文件名,主要是因为如下原因:

  • 这个值可能会通过 JavaScript 进行动态修改
  • 或者 Content-Disposition 中指定的 download 属性优先级高于 a.download

这种应该是大家最熟悉的方式了,但熟悉归熟悉,还有一些值得注意的点:

  • download 属性只适用于 同源 URL
    • 同源 URL 会进行 下载 操作
    • 非同源 URL 会进行 导航 操作
    • 非同源的资源 仍需要进行下载,那么可以将其转换为 blob: URLdata: URL 形式
  • HTTP 响应头中的 Content-Disposition 属性中指定了一个不同的文件名,那么会优先使用 Content-Disposition 中的内容
  • HTTP 若 HTTP 响应头中的 Content-Disposition 被设置为 Content-Disposition='inline',那么在 Firefox 中会优先使用 Content-Dispositiondownload 属性

静态方式:

  <a href="http://127.0.0.1:4000/pdf/2-1.png" download="2.pdf">下载</a>

动态方式:

function download(url, filename){   
const a = document.createElement("a"); 
// 创建 a 标签   
a.href = url; 
// 下载路径   
a.download = filename;  
// 下载属性,文件名   
a.style.display = "none"; // 不可见   
document.body.appendChild(a); // 挂载   
a.click(); // 触发点击事件   
document.body.removeChild(a); // 移除 
}

Blob 方式

if (reqConf.responseType == 'blob') {     
// 返回文件名     
let contentDisposition = config.headers['content-disposition'];     
if (!contentDisposition) {       
contentDisposition = `;filename=${decodeURI(config.headers.filename)}`;    
 }     
 const fileName = window.decodeURI(contentDisposition.split(`filename=`)[1]);     
 // 文件类型     
 const suffix = fileName.split('.')[1];     
 // 创建 blob 对象     
 const blob = new Blob([config.data], {       
 type: FileType[suffix],     });     
 const link = document.createElement('a');     
 link.style.display = 'none';     
 link.href = URL.createObjectURL(blob); 
 // 创建 url 对象     
 link.download = fileName; 
 // 下载后文件名     
 document.body.appendChild(link);     
 link.click();     
 document.body.removeChild(link); 
 // 移除隐藏的 a 标签      
 URL.revokeObjectURL(link.href); 
 // 销毁 url 对象   
 }

Content-dispositionlocation.href/window.open 实现下载

这看似是三种下载方式,但实际上就是一种,而且还是以 Content-disposition 为准。

Content-Disposition 响应头 指示回复的内容该以何种形式展示,是以 内联 的形式(即网页或页面的一部分)展示,还是以 附件 的形式 下载 并保存到本地,如下:

  • inline: 是 默认值,表示回复中的消息体会以页面的一部分或者整个页面的形式展示

    Content-Disposition: inline

  • attachment: 设置为此值意味着消息体应该被下载到本地,大多数浏览器会呈现一个 “保存为” 的对话框,并将 filename 的值预填为下载后的文件名

    Content-Disposition: attachment; filename="filename.jpg"

因此,基于 location.href='xxx'window.open(xxx) 的方式能实现下载就是基于 Content-Disposition: attachment; filename="filename.jpg" 的形式,又或者说是触发了浏览器本身的下载行为,满足了这个条件,无论是通过 a 标签跳转location.href 导航window.open 打开新页面直接在地址栏上输入 URL 等都可以实现下载。

H5 移动端的下载

H5 移动端针对于 预览 操作而言基于以上的方式都是可以实现,但是 下载 操作可就不同了,因为这是要区分场景:

  • 基于 手机浏览器
  • 基于 微信内置浏览器

基于 手机浏览器 的下载方式和上述提到的内容大致上也是一致的,本质上只要所在的客户端支持下载那就没有问题,然而在 微信内置浏览器 中你使用常规的下载方式可能达不到预期:

  • Android 中使用常规的下载方式,通常会弹出对话框,询问你是否需要唤醒 手机浏览器 来实现对应资源的下载,部分机型却不会
  • IOS 中以上方式都 无法实现下载,因此通常情况下会打开一个新的 webview 来提供预览,部分机型在新的页面中支持 长按屏幕 的方式进行保存操作,但并不是所有机型都支持

本质原因是在 微信内置浏览器 中屏蔽任何的 下载链接,如 APP 的下载链接普通文件 的下载链接 等等。

H5 移动端的下载还能怎么做?

由于这是 微信内置浏览器 环境对下载功能的屏蔽,因此 不用再考虑(`想都不敢想`)基于 微信内置浏览器 来实现下载功能,转而应该考虑的是如何实现 间接下载

  • 判断当前是否是属于 微信内置浏览器,若是则帮助用户自动唤起 手机浏览器 实现下载,但并不是所有机型都支持 唤起 操作,因此最好是提示使用用户直接通过 手机浏览器 实现下载,为了方便用户,可以实现 一键复制 的功能进行辅助
  • 另一种就直接提示只支持 PC 端下载,放弃对移动端的下载操作

最后

综上所述,实际在实现 pdf 预览的过程中可能暂时没有办法达到完美的方式,特别是针对类似 发票类pdf 文件,仍存在如下的问题:

  • 无法保证 h5 移动端都具备 下载 功能
  • 无法保证 pdf 预览 时,预览的字体和实际发票 字体 保持一致

现有大部分的预览方式都基于 pdf.js 的方式实现,而 pdf.js 内部通过 PDFJs.getDocument(url/buffer) 的方式基于 文件地址数据流 来获取内容,再通过 canvas 处理渲染 pdf 文件,感兴趣可以去研究 pdf.js 源码。

pdf.js 带来相关问题就是如果对应的 pdf 文件中包含了 pdf.js 中不存在的字体,那么就无法完整渲染,另外渲染出来的字体和原本的 pdf 文件字体会存在差异。

针对这两点,目前发现谷歌内置的 pdf 插件似乎提供了很好的支持,意味着其他浏览器如果包含了谷歌相关的插件(如:Edge、QQ Browser),就可以直接基于 <iframe> 的方式实现预览,又或者为了更严谨字体一致性只能通过下载的方式来查看源文件。

实现不了产品的要求怎么办?

例如上述探讨的方案其实无法满足文章开头提到的部分要求。产品提出需求的目的也是为了提供更好的用户体验(`正常情况下`),但是这些要求仍然要落实到技术上,而技术支持程度如何需要我们及时反馈(`除非你的产品是技术经验`),因此作为开发者你需要提供充足的内容向产品证明,然后自己再给出一些间接实现的方案(又或者产品自己就给出新的方案),看是否符合 第二预期,核心就是 合理沟通 + 其他方案每个人的处境不同,实际情况也许 ... 懂得都懂)。

9E38506E.jpg

以上是个人的一些看法和理解,有不当之处,可以在评论区指正!!!

希望本文对你有所帮助!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/571417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java-Crawler】SpringBoot集成WebMagic实现爬虫出现的问题集(一)

SpringBoot集成WebMagic实现爬虫出现的问题集&#xff08;一&#xff09; 一、SpringBoot集成WebMagic框架日志异常问题及解决方案二、使用 Firefox 驱动&#xff08;geckodriver&#xff09;三、设置WebMagic中site中的User-Agent&#xff08;避免反爬虫&#xff09; 一、Spri…

【网络编程】demo版TCP网络服务器实现

文章目录 一、引入二、服务端实现2.1 创建套接字socket2.2 绑定bind2.3 设置监听状态listen2.4 获取新链接accept2.5 获取信息与返回信息&#xff08;文件操作&#xff09; 三、客户端实现3.1 创建套接字socket3.2 绑定问题3.3 发起链接connect3.4 客户端并行3.4.1 多进程版3.4…

公网远程访问本地Jupyter Notebook服务

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 转载自cpolar的文章&#xff1a;公网远程访问Jupyter Notebook【Cpolar内网穿透】 前言 Jupyter Notebook&am…

针对UDP协议的攻击与防御

一、UDP协议概述 UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;是TCP/IP协议栈中的一种无连接的传输协议&#xff0c;能够提供面向事务的简单不可靠数据传输服务。 1&#xff0e;UDP的报文格式 UDP的报文格式如图1所示。 图1 UDP报文格式 …

怎么在pdf文件上添加水印

怎么在pdf文件上添加水印&#xff1f;PDF添加水印是一种十分实用的方式&#xff0c;可以大大提高PDF文档的安全性和防护能力。在实际操作中&#xff0c;我们可以根据具体需求在PDF文件的各个页面上添加水印。这样即使你的PDF文件被他人恶意盗用&#xff0c;也可以快速、准确地找…

堤防安全自动化监测系统

项目背景 我国河系众多&#xff0c;海岸线漫长&#xff0c;在江边、海边修筑修筑着几万公里的提防设施保卫着沿江、沿海居民的生命安全&#xff0c;也保卫着经济发展的累累硕果。近年来&#xff0c;政府加大了堤防建设改造力度&#xff0c;提高了部分堤段的防洪能力。同时&…

统信UOS系统开发笔记(一):国产统信UOS系统搭建开发环境之虚拟机安装

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/130876940 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

vue 弹窗实现方法

Vue实现弹窗的方法有很多种&#xff0c;这里给出一个简单的示例&#xff1a; 1. 首先&#xff0c;在Vue项目中创建一个名为Modal.vue的组件文件&#xff1a; html <template> <div class"modal-mask" v-show"visible" click.self"close"…

基于遗传算法和非线性规划的函数寻优算法(matlab实现)

以下内容大部分来源于《MATLAB智能算法30个案例分析》&#xff0c;仅为学习交流所用。 1 理论基础 1.1 非线性规划 非线性规划是20世纪50年代形成的一门新兴学科。1951年库恩和塔克发表的关于最优性条件(后来称为库恩塔克条件)的论文是非线性规划诞生的标志。非线性规划研究…

报表控件FastReport使用指南-在Ubuntu LTS中创建PDF文档

FastReport 是功能齐全的报表控件&#xff0c;可以帮助开发者可以快速并高效地为.NET&#xff0c;VCL&#xff0c;COM&#xff0c;ActiveX应用程序添加报表支持&#xff0c;由于其独特的编程原则&#xff0c;现在已经成为了Delphi平台最优秀的报表控件&#xff0c;支持将编程开…

如何避免Salesforce Apex代码中5个常见错误,提升开发技巧?

编码是一门需要严谨和谨慎的技术&#xff0c;即使是有经验的开发人员也会犯错。一些最常见的编程错误&#xff0c;可能会导致严重的后果。因此&#xff0c;作为一名开发人员&#xff0c;了解并避免这些错误是非常重要的。 本篇文章将为学习者介绍在编写Apex代码时一定要规避的…

Java核心锁基准测试

测试模型 基于JMH基准测试库 测试代码 package com.lsy.study.benchmark;import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.results.format.ResultFormatType; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.Options; import…

《人工智能算法案例大全:基于Python》——实践AI算法,驭智创新之路

导语 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;AI算法成为推动智能化进程的核心要素。而在这个领域中&#xff0c;一本名为《人工智能算法案例大全&#xff1a;基于Python》的书籍引起了广泛关注。本文将深入探讨这本书所呈现的丰富案例&#xff0c;…

Linux——网络套接字2|Tcp服务器编写

本篇博客先看后面的代码,再回来看上面这些内容。 .hpp文件,基本调用 服务器基本框架

Ubuntu下Docker部署Gitlab CI

1. ubuntu gitlab安装步骤 1.1 更新系统软件包列表&#xff1a; sudo apt update1.2 安装必要的依赖项&#xff1a; sudo apt install curl openssh-server ca-certificates tzdata perl1.3 下载并安装 GitLab 包&#xff1a; curl -LO https://packages.gitlab.com/instal…

Springboot2.5.x版本之自动创建(H2/DERBY/HSQL)数据源源码分析-yellowcong

场景&#xff1a;当我们没有配置mysql&#xff0c;postgresql等数据源的时候&#xff0c;pom.xml里面引入了H2/DERBY/HSQL jar包&#xff0c;也没有配置连接&#xff0c;却有数据源创建的情况。 springboot启动的第一步 1.DataSourceAutoConfiguration 配置类启动 2.DataSource…

DolphinScheduler远程启动任务

我本地有JAVA程序&#xff0c;需要调用DolphinScheduler的接口启动任务&#xff0c;动态去调用 1、DolphinScheduler的内容逻辑关系 先明确DolphinScheduler内部任务的逻辑关系 项目 |——工作流 |——任务&#xff08;节点&#xff09; 我起的是工作流&#xff0c;一个任务完…

Transformer仅有自注意力还不够?微软联合巴斯大学提出频域混合注意力SpectFormer

本文介绍一篇来自英国巴斯大学&#xff08;University of Bath&#xff09;与微软合作完成的工作&#xff0c;研究者从频率域角度入手探究视觉Transformer结构中的频域注意力和多头注意力在视觉任务中各自扮演的作用。 论文链接&#xff1a; https://arxiv.org/abs/2304.06446 …

为什么都说测试岗是巨坑,趁早跳出去?10年测试人告诉你千万别上当了...

每次都有人问我软件测试的前景是什么样的&#xff0c;每年也会有人很多人纷纷涌入测试的岗位上&#xff0c;希望自己能够进入阿里、华为等大厂。 但是测试岗位真的那么吃香吗&#xff1f;今天我结合从零基础小白到测试开发的成长经历&#xff0c;来说下这个行业的发展前景&…

vmware安装debian 11.7.0 配置LVM

vmware安装debian 11.7.0 配置LVM 1、下载镜像2、创建并安装debian 11虚拟机2.1、选择 Graphical install2.2、选择安装过程显示语言和系统语言2.3、选择地区2.4、键盘映射2.5、设置主机名-debian2.6、设置网络-直接跳过2.7、设置root密码2.8、创建普通账户2.9、为普通账户设置…