前端学习笔记之文件下载(1.0)

因为要用到这样一个场景,需要下载系统的使用教程,所以在前端项目中就提供了一个能够下载系统教程的一个按钮,供使用者进行下载。

所以就试着写一下这个功能,以一个demo的形式进行演示,在学习的过程中也发现了中文路径的严重问题!!!!!!!!!!!!

首先描述一下接下来展示的代码的效果:

就是在界面上又对应的文本和下载对应文件的按钮,这里因为是1.0版本,就不对实现这个功能做成函数进行封装了,如果有时间的话还可以封装成一个函数,通过传参来下载指定的文件。

这里就是真正的符合应用场景的前端通过发送请求来下载服务器上的文件这样的场景,而不是通过ZZAI回答的那样使用window.location.href的方式,吐槽一波

直接上完整的代码,随后进行介绍

后端代码【node.js】:

const express = require('express');
const path = require('path');
const cors = require('cors'); // 引入 CORS 中间件
const fs=require('fs');
const app = express();
const PORT = 3001;
// 启用 CORS,允许所有域访问(仅用于测试,生产环境中应限制允许的域)
app.use(cors());// 设置静态文件的目录为 public
app.use(express.static(path.join(__dirname, 'public')));
console.log(12,path.join(__dirname, 'public/操作手册.pdf'))
// 定义一个路由来返回文件的 URL 而不是直接发送文件
app.get('/api/download-url/操作手册.pdf', (req, res) => {const filePath = path.join(__dirname, 'public/操作手册.pdf');res.json({ fileUrl: `http://localhost:${PORT}/download/操作手册.pdf` });
});
// 修改后的路由,用于返回 abc.txt 文件的内容
app.get('/check-file/abc.txt', (req, res) => {const filePath = path.join(__dirname, 'public/abc.txt');fs.readFile(filePath, 'utf8', (err, data) => {if (err) {if (err.code === 'ENOENT') {res.status(404).send('文件不存在');} else {res.status(500).send('读取文件时出错: ' + err.message);}} else {res.send(data); // 返回文件内容}});
});
app.get('/download/abc.txt', (req, res) => {const filePath = path.join(__dirname, 'public/abc.txt');fs.readFile(filePath, (err, data) => {if (err) {res.status(500).send('文件读取错误');} else {res.setHeader('Content-Disposition', 'attachment; filename="abc.txt"');res.set('Content-Type', 'text/plain');res.send(data);}});
});
app.get('/download/操作手册.pdf', (req, res) => {const filePath = path.join(__dirname, 'public/操作手册.pdf');fs.readFile(filePath, (err, data) => {if (err) {res.status(500).send('文件读取错误');} else {res.setHeader('Content-Disposition', 'attachment; filename="操作手册.pdf"');res.set('Content-Type', 'application/pdf');res.send(data);}});
});
app.get('/download/b.pdf', (req, res) => {const filePath = path.join(__dirname, 'public/b.pdf');fs.readFile(filePath, (err, data) => {if (err) {res.status(500).send('文件读取错误');} else {res.setHeader('Content-Disposition', 'attachment; filename="b.pdf"');res.set('Content-Type', 'application/pdf');res.send(data);}});
});
app.get('/download/a.pdf', (req, res) => {const filePath = path.join(__dirname, 'public/a.pdf');fs.readFile(filePath, (err, data) => {if (err) {res.status(500).send('文件读取错误');} else {res.setHeader('Content-Disposition', 'attachment; filename="a.pdf"');res.set('Content-Type', 'application/pdf');res.send(data);}});
});
app.get('/download/一.pdf', (req, res) => {const filePath = path.join(__dirname, 'public/一.pdf');fs.readFile(filePath, (err, data) => {if (err) {res.status(500).send('文件读取错误');} else {res.setHeader('Content-Disposition', 'attachment; filename="一.pdf"');res.set('Content-Type', 'application/pdf');res.send(data);}});
});
// 启动服务器
app.listen(PORT, () => {console.log(`服务器启动成功,访问地址为:http://localhost:${PORT}`);
});

后端是用node写的服务器,因为这些语法还是js,所以对于前端人员来说还是比较好理解的,使用的是express框架。

前端访问界面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>文件下载示例</title>
</head>
<body><h1>点击按钮下载 abc.txt</h1><button id="downloadButton">下载 abc.txt</button><h1>点击按钮下载 a.pdf</h1><button id="downloadButton2">a.pdf</button><h1>点击按钮下载 操作手册.pdf</h1><button id="downloadButton3">下载操作手册.pdf</button><h1>点击按钮下载 操作手册a.pdf</h1><button id="downloadButton4">b.pdf</button><h1>点击按钮下载 操作手册一.pdf</h1><button id="downloadButton5">一.pdf</button><script>document.getElementById("downloadButton").addEventListener("click", async function() {try {const response = await fetch(`http://localhost:3001/download/abc.txt`);if (!response.ok) {throw new Error('Network response was not ok');}const blob = await response.blob();const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.style.display = 'none';a.href = url;a.download = 'abc.txt';document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url);document.body.removeChild(a);} catch (error) {console.error('下载文件时出错:', error);}});document.getElementById("downloadButton2").addEventListener("click", async function() {try {const response = await fetch(`http://localhost:3001/download/a.pdf`);if (!response.ok) {throw new Error('Network response was not ok');}const blob = await response.blob();const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.style.display = 'none';a.href = url;a.download = 'a.pdf'; // 保持文件名和扩展名不变document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url);document.body.removeChild(a);} catch (error) {console.error('下载a.pdf 文件时出错:', error);}});document.getElementById("downloadButton3").addEventListener("click", async function() {try {const response = await fetch(`http://localhost:3001/download/操作手册.pdf`);if (!response.ok) {throw new Error('Network response was not ok');}const blob = await response.blob();const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.style.display = 'none';a.href = url;a.download = '操作手册.pdf'; // 保持文件名和扩展名不变document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url);document.body.removeChild(a);} catch (error) {console.error('下载操作手册.pdf 文件时出错:', error);}});document.getElementById("downloadButton4").addEventListener("click", async function() {try {const response = await fetch(`http://localhost:3001/download/b.pdf`);if (!response.ok) {throw new Error('Network response was not ok');}const blob = await response.blob();const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.style.display = 'none';a.href = url;a.download = 'b.pdf'; // 保持文件名和扩展名不变document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url);document.body.removeChild(a);} catch (error) {console.error('b.pdf 文件时出错:', error);}});document.getElementById("downloadButton5").addEventListener("click", async function() {try {const response = await fetch(`http://localhost:3001/download/一.pdf`);if (!response.ok) {throw new Error('Network response was not ok');}const blob = await response.blob();const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.style.display = 'none';a.href = url;a.download = '一.pdf'; // 保持文件名和扩展名不变document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url);document.body.removeChild(a);} catch (error) {console.error('一.pdf 文件时出错:', error);}});</script>
</body>
</html>

 来看运行的效果:

 当我们点击下载abc.txt按钮的时候,对应的文件就会下载

因为我下载了多个同名文件,自动重命名为abc(2).txt。

来看服务器上的文件结构:

 

 我这里下载的就是服务器上的public文件夹中的文件,因为这样符合实际,因为我们要下载的资源肯定是要上传的远程服务器上,不可能在本地。这里我们可以再本地下载服务器上的文件。

因为一开始写的版本是针对文本文件的,所以设置的请求头和content-type会和下面的pdf文件类型的设置会不一样:

然后测试pdf文件的下载,也是可以正常下载的,因为一开始我的pdf是中文命名的,所以就会出现了这个错误【下载操作手册.pdf】:

 

于是就开始检查代码,发现并没有问题,一开始试着从网上找问题,发现可能是中文导致的问题,所以就试着把中文改成了英文,发现,一样的代码,改成了英文命名,就可以成功下载。

于是求助于WXYY,它告诉了我可能的原因,然后我就进行了多次尝试:

路径正确。浏览器这是Chrome,程序员公认最好的浏览器,没有之一,于是试着换Lenovo浏览器,不用说,还是一样的效果。

 

服务器日志那种东西更是很难看懂的,直接pass,毕竟还是没有专业到能通过看懂日志来解决问题的地步!

然后简化文件名:我也试了 ,将文件名改成一.pdf,这样就更简单了,发现还是不行

对于删除重复的路由处理函数,当我发现他这样说的时候,我就将重复代码给删了,还是一样的结果。

更新浏览器或node,这个浏览器就是最新的,可能就是小版本差异,这个影响直接忽略,对于node,突然想到因为做项目调成了老版本的node

 

那就开整,调成20,没必要是最新,这个版本完全足够,那就改一下版本试一试,顺便说一下,nvm是真好用,如果没了它,卸载node,再安装新版本,那就没那个必要了,这里我们直接切换node版本。

 

然后我们再启动node服务器,看一下,这下如果再不行,那就说明中文是真的不行!!!!!!!!!!!!!!!!!!

 

 

一毛一样,还是放弃,这个中文是真的不行,就像你看url栏中有中文吗?除了那种携带的参数在?后面传递过来的中文,在url中也会转成 url编码。

说在最后,介绍一下URL编码:

当你在浏览器中输入包含中文字符的URL时,浏览器会自动将这些中文字符进行URL编码(也称为百分号编码或百分比编码)。URL编码是一种将非ASCII字符转换为可以在URL中安全传输的格式的方法。

在你提供的例子中,“操作手册.pdf”被编码为“%E6%93%8D%E4%BD%9C%E6%89%8B%E5%86%8C.pdf”。这是因为在UTF-8编码中,“操”字的编码是E6 93 8D,“作”字的编码是E4 BD 9C,以此类推,每个中文字符都被转换成了三个百分号后跟两个十六进制数的形式。

URL编码(URL Encoding),也称为百分号编码(Percent-Encoding),是一种用于将非ASCII字符或特定字符转换为可在统一资源定位符(URL)中安全传输的格式的方法。这种编码方法使用百分号(%)后跟两位十六进制数来表示原始字符。URL编码是互联网上数据交换的一种标准方式,特别是在处理包含特殊字符的URL、表单数据或任何需要在互联网上传输的文本时。

为什么需要URL编码?

  1. 字符集限制:URL只能包含ASCII字符集。URL编码允许非ASCII字符(如中文字符、特殊符号等)被安全地传输。

  2. 保留字符:URL中包含一些具有特殊意义的字符,如?=&等。这些字符在URL中有特定的用途(如分隔参数、赋值等)。为了避免歧义,这些特殊字符在需要作为普通文本传输时也需要进行URL编码。

URL编码规则

  • 空格字符编码为+号或者%20

  • 非ASCII字符和特殊字符(如@#$%&+,/:;=?[]"等)转换为%后跟两位十六进制数。

  • 对于ASCII字符,通常不需要编码,但某些字符(如空格)根据上下文可能需要编码。

示例

假设我们有一个包含中文字符和特殊字符的字符串:“你好 & 世界”。在URL编码后,它变为:%E4%BD%A0%E5%A5%BD%20%26%20%E4%B8%96%E7%95%8C

  • “你”在UTF-8编码下是E4 BD A0,所以编码后为%E4%BD%A0

  • “好”在UTF-8编码下是E5 A5 BD,所以编码后为%E5%A5%BD

  • 空格编码为%20

  • “&”编码为%26

  • “世”在UTF-8编码下是E4 B8 96,所以编码后为%E4%B8%96

  • “界”在UTF-8编码下是E7 95 8C,所以编码后为%E7%95%8C

在Web开发中的应用

  • URL参数:当在URL中传递参数时,参数名和参数值通常需要进行URL编码。

  • 表单提交:在HTML表单中,当enctype属性设置为application/x-www-form-urlencoded时,表单数据在发送到服务器之前会进行URL编码。

  • AJAX请求:在发送AJAX请求时,如果请求的数据包含特殊字符,也需要进行URL编码。

URL编码是Web开发中不可或缺的一部分,它确保了数据在不同系统之间的安全、可靠传输。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA实现GTP光口视频转USB3.0传输,基于FT601+Aurora 8b/10b编解码架构,提供3套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案本博已有的FPGA驱动USB通信方案 3、工程详细设计方案工程设计原理框图输入Sensor之-->OV5640摄像头输入Sensor之-->芯片解码的…

借助nntools的模型训练(图像去噪任务, PyTorch)

在跑通开源代码的过程中&#xff0c;一直难以找到很合适的代码框架或代码模板&#xff0c;尤其对于初学者而言是不小的挑战。本人在跑代码的过程中&#xff0c;发现在GitHub上有一个辅助训练过程的模板。这篇文章主要记录它的使用方法&#xff0c;并标记来源。 代码均在jupyter…

C# 常量

文章目录 前言一、整数常量&#xff08;一&#xff09;合法与非法实例对比&#xff08;二&#xff09;不同进制及类型示例 二、浮点常量三、字符常量四、字符串常量五、定义常量 前言 在 C# 编程的世界里&#xff0c;常量是一类特殊的数据元素&#xff0c;它们如同程序中的 “定…

PHP 生成分享海报

因为用户端有多个平台&#xff0c;如果做分享海报生成&#xff0c;需要三端都来做&#xff0c;工作量比较大。 所以这个艰巨的任务就光荣的交给后端了。经过一定时间的研究和调试&#xff0c;最终圆满完成了任务&#xff0c;生成分享海报图片实现笔记如下。 目录 准备字体文件…

探索Python词云库WordCloud的奥秘

文章目录 探索Python词云库WordCloud的奥秘1. 背景介绍&#xff1a;为何选择WordCloud&#xff1f;2. WordCloud库简介3. 安装WordCloud库4. 简单函数使用方法5. 应用场景示例6. 常见Bug及解决方案7. 总结 探索Python词云库WordCloud的奥秘 1. 背景介绍&#xff1a;为何选择Wo…

git 学习笔记

目录 一、git 前期配置准备 1、托管平台的账号注册&#xff08;以gitee码云为demo&#xff09; 2、本地个人电脑配置 &#xff08;1&#xff09;配置用户属性 ​编辑 &#xff08;2&#xff09;配置SSH密钥 二、git 工作流程图 三、git 提交命令 &#xff08;1&#x…

uniapp中使用uni-forms实现表单管理,验证表单

前言 uni-forms 是一个用于表单管理的组件。它提供了一种简化和统一的方式来处理表单数据&#xff0c;包括表单验证、字段绑定和提交逻辑等。使用 uni-forms可以方便地创建各种类型的表单&#xff0c;支持数据双向绑定&#xff0c;可以与其他组件及API进行良好的集成。开发者可…

_C#_串口助手_字符串拼接缺失问题(未知原理)

最近使用WPF开发串口助手时&#xff0c;遇到一个很奇怪的问题&#xff0c;无论是主线程、异步还是多线程&#xff0c;当串口接收速度达到0.016s一次以上&#xff0c;就会发生字符串缺失问题并且很卡。而0.016s就一切如常&#xff0c;仿佛0.015s与0.016s是天堑之隔。 同一份代码…

JVM(JAVA虚拟机)内存溢出导致内存不足,Java运行时环境无法继续

1、先贴出服务最后打印出来的日志&#xff0c;意思就是给虚拟机分配的内存被用完了&#xff0c;没有可用的内存了&#xff0c;服务运行不了了&#xff0c;被动停服了。详细的日志记录在了/home/user/zx/tomcat/apache-tomcat-8.5.82/bin/hs_err_pid147951.log文件里。 Java Ho…

OGRE 3D----3. OGRE绘制自定义模型

在使用OGRE进行开发时,绘制自定义模型是一个常见的需求。本文将介绍如何使用OGRE的ManualObject类来创建和绘制自定义模型。通过ManualObject,开发者可以直接定义顶点、法线、纹理坐标等,从而灵活地构建各种复杂的几何体。 Ogre::ManualObject 是 Ogre3D 引擎中的一个类,用…

Spring Boot英语知识网站:架构与开发

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

如何借助AI生成PPT,让创作轻松又高效

PPT是现代职场中不可或缺的表达工具&#xff0c;但同时也可能是令人抓狂的时间杀手。几页幻灯片的制作&#xff0c;常常需要花费数小时调整字体、配色与排版。AI的飞速发展为我们带来了革新——AI生成PPT的技术不仅让制作流程大大简化&#xff0c;还重新定义了效率与创意的关系…

Unity ShaderLab 实现网格爆炸

实现思路&#xff1a; 沿着3D物体每个面的法线&#xff0c;将面偏移一定的位置。 Shader Graph实现如下&#xff1a; Shader Lab 实现如下&#xff1a; Shader "Unlit/MeshExplode" {Properties{_MainTex ("Texture", 2D) "white" {}_Distan…

阅读《基于蒙特卡洛法的破片打击无人机易损性分析》_笔记

目录 基本信息 1 引言 1.1 主要研究内容 1.2 研究必要性&#xff08;为什么要研究&#xff09; 1.3 该领域研究现状&#xff08;别人做了什么/怎么做的&#xff09; 2 主要研究过程&#xff08;我们做了什么&#xff09; 2.1 建立目标仿真模型 2.2 确定毁伤依据 2.3 无…

如何让控件始终处于父容器的居中位置(父容器可任意改变大小)

1、改变父容器大小前 父容器是一个panel&#xff0c;控件是一个按钮button1 1&#xff09;刚开始让button1的左边距离panel的左边缘和button1的右边距离panel的右边缘两个距离相等&#xff1b; 2&#xff09;将button1的Anchor属性设置为None 2、改变父容器大小后 直接改变…

【线程】Java多线程代码案例(2)

【线程】Java多线程代码案例&#xff08;2&#xff09; 一、定时器的实现1.1Java标准库定时器1.2 定时器的实现 二、线程池的实现2.1 线程池2.2 Java标准库中的线程池2.3 线程池的实现 一、定时器的实现 1.1Java标准库定时器 import java.util.Timer; import java.util.Timer…

非递归遍历二叉树(数据结构)

我的博客主页 非递归遍历二叉树 前序遍历&#xff08;迭代&#xff09;中序遍历&#xff08;迭代&#xff09;后续遍历&#xff08;迭代&#xff09; 二叉树的遍历方式有&#xff1a;前序遍历、中序遍历、后续遍历&#xff0c;层序遍历&#xff0c;而树的大部分情况下都是通过递…

【CSS】一篇掌握CSS

不是因为有了希望才去坚持,而是坚持了才有了希望 目录 一.导入方式 1.行内样式 2.内部样式 3.外部样式(常用) 二.选择器 1.基本选择器(常用) 1.1标签选择器 1.2类选择器 1.3id选择器 2.层次选择器 2.1后代选择器 2.2子选择器 2.3相邻兄弟选择器 2.4通用兄弟选择器…

封装类与封装函数

目录结构 src/ ├── utils/ │ ├── test.js │ ├── Calculator.js ├── views/ │ ├── Home.vue ├── App.vue 共同点&#xff1a;模块导出与模块引入 封装函数 场景 简单、轻量级和性能敏感的场景&#xff0c;适合快速开发和维护。 优 可维护性&…

【论文阅读】Federated learning backdoor attack detection with persistence diagram

目的&#xff1a;检测联邦学习环境下&#xff0c;上传上来的模型是不是恶意的。 1、将一个模型转换为|L|个PD,&#xff08;其中|L|为层数&#xff09; 如何将每一层转换成一个PD&#xff1f; 为了评估第&#x1d457;层的激活值&#xff0c;我们需要&#x1d450;个输入来获…