前端学习笔记之文件下载(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.mzph.cn/diannao/62352.shtml

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

相关文章

【阅读记录-章节4】Build a Large Language Model (From Scratch)

文章目录 4. Implementing a GPT model from scratch to generate text4.1 Coding an LLM architecture4.1.1 配置小型 GPT-2 模型4.1.2 DummyGPTModel代码示例4.1.3 准备输入数据并初始化 GPT 模型4.1.4 初始化并运行 GPT 模型 4.2 Normalizing activations with layer normal…

浅谈——深度学习和马尔可夫决策过程

深度学习是一种机器学习方法&#xff0c;它通过模拟大脑的神经网络来进行数据分析和预测。它由多层“神经元”组成&#xff0c;每一层从数据中提取出不同的特征。多层次的结构使得深度学习模型可以捕捉到数据中的复杂关系&#xff0c;特别适合处理图片、语音等复杂数据。 马尔可…

Python PDF转JPG图片小工具

Python PDF转JPG图片小工具 1.简介 将单个pdf装换成jpg格式图片 Tip: 1、软件窗口默认最前端&#xff0c;不支持调整窗口大小&#xff1b; 2、可通过按钮选择PDF文件&#xff0c;也可以直接拖拽文件到窗口&#xff1b; 3、转换质量有5个档位&#xff0c;&#xff08;0.25&a…

「qt交叉编译arm64」支持xcb、X11

完整的交叉编译好支持xcb的qt库&#xff08;qt5.15.2、arm64、xcb、no-opengl&#xff09; 已安装xcb、X11库的交叉编译器&#xff08;x86_64-aarch64-linux-gnu&#xff09; 文章目录 1. 修改qmake.conf&#xff0c;指定交叉编译器2. 配置编译选项&#xff0c;执行configureL…

使用SOAtest进行功能回归测试

持续集成是将所有开发人员的工作副本合并到共享的主线上。这个过程使软件开发对开发人员来说更容易访问、更快、风险更小。 阅读这篇文章&#xff0c;让我们了解如何配置Parasoft SOAtest作为持续集成过程的一部分&#xff0c;来执行功能测试和回归测试。我们将介绍如何使用主…

ais_server 学习笔记

ais_server 学习笔记 一前序二、ais init1、时序图如下2. 初始化一共分为以下几个重要步骤&#xff1a;2.1.1、在ais_server中启动main函数&#xff0c;然后创建AisEngine&#xff0c;接着初始化AisEngine2.1.2、解析/var/camera_config.xml 文件&#xff0c;获取相关配置参数。…

L1G3000 任务-浦语提示词工程

基础任务 (完成此任务即完成闯关) 背景问题&#xff1a;近期相关研究指出&#xff0c;在处理特定文本分析任务时&#xff0c;语言模型的表现有时会遇到挑战&#xff0c;例如在分析单词内部的具体字母数量时可能会出现错误。任务要求&#xff1a;利用对提示词的精确设计&#xf…

Unity之一键创建自定义Package包

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之一键创建自定义Package包 TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取&#xff01; …

Redis开发04:Redis的INFO信息解析

命令解释redis_versionRedis 的版本号&#xff0c;这里是 3.2.100。redis_git_sha1Redis 使用的 Git SHA1 校验值&#xff0c;表示当前代码的版本。redis_git_dirty如果 Redis 当前运行的代码是脏版本&#xff08;未提交的修改&#xff09;&#xff0c;该值为 1&#xff0c;否则…

python的Flask框架使用

python的Flask框架使用 python环境搭建conda安装python自带的虚拟环境&#xff1a;venv python环境搭建 官网地址 点击downloads 选择你需要的版本&#xff0c;我这里使用的3.12.6 选择Windows installer (64-bit) 选择自定义安装&#xff0c;勾选以管理员权限安装&#xff0…

网络原理(一)—— http

什么是 http http 是一个应用层协议&#xff0c;全称为“超文本传输协议”。 http 自 1991 年诞生&#xff0c;目前已经发展为最主流使用的一种应用层协议。 HTTP 往往基于传输层的 TCP 协议实现的&#xff0c;例如 http1.0&#xff0c;http1.0&#xff0c;http2.0 http3 是…

CTF之密码学(Polybius密码)

棋盘密码&#xff0c;也称为Polybius密码或方格密码&#xff0c;是一种基于替换的加密方法。以下是对棋盘密码的详细解析&#xff1a; 一、加密原理 棋盘密码使用一个5x5的方格棋盘&#xff0c;其中填充了26个英文字母&#xff08;通常i和j被视为同一个字母并放在同一个格子中…

二刷代码随想录第16天

513. 找树左下角的值 找到深度最大的点&#xff0c;遍历方式左边节点在右边节点前面&#xff0c;找到就返回&#xff0c;一定就是最左下角的值了 class Solution { public:int max_depth -1;int result 0;int findBottomLeftValue(TreeNode* root) {traversal(root, 0);ret…

Dockerfile docker-compose

1、Dockerfile # 使用官方的Python作为基础镜像 FROM python:3.9 # 设置工作目录 WORKDIR /app # 复制当前目录下的所有文件到容器的工作目录中 COPY . /app # 安装所需的Python库 #RUN pip install --no-cache-dir -r requirements.txt # 复制 requirements.txt 并安装依赖…

103.【C语言】数据结构之建堆的时间复杂度分析

1.向下调整的时间复杂度 推导 设树高为h 发现如下规律 按最坏的情况考虑(即调整次数最多) 第1层,有个节点,最多向上调整h-1次 第2层,有个节点,最多向上调整h-2次 第3层,有个节点,最多向上调整h-3次 第4层,有个节点,最多向上调整h-4次 ... 第h-1层,有个节点,最多向上调整1次 第…

用Python爬虫“偷窥”1688商品详情:一场数据的奇妙冒险

引言&#xff1a;数据的宝藏 在这个信息爆炸的时代&#xff0c;数据就像是一座座等待挖掘的宝藏。而对于我们这些电商界的探险家来说&#xff0c;1688上的商品详情就是那些闪闪发光的金子。今天&#xff0c;我们将化身为数据的海盗&#xff0c;用Python这把锋利的剑&#xff0…

Python基础学习-12匿名函数lambda和map、filter

目录 1、匿名函数&#xff1a; lambda 2、Lambda的参数类型 3、map、 filter 4、本节总结 1、匿名函数&#xff1a; lambda 1&#xff09;语法&#xff1a; lambda arg1, arg2, …, argN : expression using arg 2&#xff09; lambda是一个表达式&#xff0c;而不是一个语…

速盾高防cdn支持移动端独立缓存

随着移动互联网的快速发展&#xff0c;移动端网页访问量也越来越大。然而&#xff0c;移动端的网络环境相对不稳定&#xff0c;用户体验可能会受到影响。因此&#xff0c;使用高防CDN来加速移动端网页访问&#xff0c;成为越来越多网站运营者的首选。 速盾高防CDN是一种分布式…

【JavaEE初阶 — 网络编程】TCP流套接字编程

TCP流套接字编程 1. TCP &#xff06; UDP 的区别 TCP 的核心特点是面向字节流&#xff0c;读写数据的基本单位是字节 byte 2 API介绍 2.1 ServerSocket 定义 ServerSocket 是创建 TCP 服务端 Socket 的API。 构造方法 方法签名 方法说明 ServerS…

idea新建springboot web项目

idea新建springboot web项目 写在前面开始项目结构定义依赖初始化创建完成修复配置文件内容乱码修改配置文件名称更新配置文件内容为yml格式 配置项目启动项启动项目 写在前面 以下操作以IntelliJ IDEA 2022.3.3版本为例&#xff0c;其他版本应该大体相似。 开始 项目结构定义…