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

news2025/1/15 11:38:18

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

所以就试着写一下这个功能,以一个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.coloradmin.cn/o/2250803.html

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

相关文章

【力扣】387.字符串中的第一个唯一字符

问题描述 思路解析 对于这种个数有限的问题&#xff0c;我的第一想法是使用桶排序来解决因为s中只有小写英文单词&#xff0c;所以我只需要一个26个单位的数组就好了通过 charAt() 来提取单个字符&#xff0c;然后通过 -‘a’ 来将其映射到 0~25&#xff0c;不然数组长度就被超…

数据采集中,除了IP池的IP被封,还有哪些常见问题?

在数据采集的过程中&#xff0c;代理IP池的使用无疑为我们打开了一扇通往信息宝库的大门。然而&#xff0c;除了IP被封禁这一常见问题外&#xff0c;还有许多其他问题可能影响数据采集的效果。本文将探讨在数据采集中&#xff0c;除了IP被封之外&#xff0c;还可能遇到的一些常…

【笔记】自动驾驶预测与决策规划_Part8_数据驱动的规划方法

文章目录 0. 前言1.生成模型1.1 Diffusion-ES1. Diffusion-ES算法介绍2. Diffusion-ES算法具体流程Diffusion Model 是什么&#xff1f;Diffusion-ES: Evolutionary StrategiesDiffusion-ES MethodDiffusion-ES Mapping Language instructions to reward functions with LLM pr…

里氏替换原则:Java面向对象设计的基石

在面向对象编程&#xff08;OOP&#xff09;中&#xff0c;继承是一个强大的工具&#xff0c;它允许我们创建新的类&#xff08;子类&#xff09;来复用和扩展现有类&#xff08;父类&#xff09;的功能。然而&#xff0c;继承也带来了复杂性&#xff0c;特别是在确保子类能够正…

C++笔记之单例模式与静态方法的使用辨析及代码规范

C++笔记之单例模式与静态方法的使用辨析及代码规范 code review! 文章目录 C++笔记之单例模式与静态方法的使用辨析及代码规范一.示例代码二.讲解2.1.代码规范2.1.1.单例模式实现2.1.2.静态方法实现2.1.3.单例模式结合静态方法2.2.总结一.示例代码 // 使用 set 方法设置值(通…

18:(标准库)DMA二:DMA+串口收发数据

DMA串口收发数据 1、DMA串口发送数据2、DMA中断串口接收定长数据包3、串口空闲中断DMA接收不定长数据包4、串口空闲中断DMA接收不定长数据包DMA发送数据包 1、DMA串口发送数据 当串口的波特率大于115200时&#xff0c;可以通过DMA1进行数据搬运&#xff0c;以防止数据的丢失。如…

加载不同本地gltf模型,模型内容不更新的解决方案

相关链接 http://mars3d.cn/editor-vue.html?keyex_6_2_2&idlayer-graphic/draw/draw-model 问题内容 加载本地gltf模型的时候&#xff0c;不clear图层&#xff0c;再打开其他本地gltf&#xff0c;gltf的内容就不更新 重现步骤 进入官网示例&#xff0c;贴入以下代码…

可视化建模以及UML期末复习篇----相关软件安装

作为一个过来人&#xff0c;我的建议是别过来。 一、可视化建模 <1>定义: 官方&#xff1a;一种使用图形符号来表示系统结构和行为的建模技术。 我&#xff1a;其实说白了就是把工作流程用图形画出来。懂不&#xff1f; <2>作用: 提高理解和分析复杂系统的能力。促…

AI开发 - GPT之魂 用Python 演示chatGPT的自注意力机制 - 机器学习

自注意力机制&#xff08;Self-Attention&#xff09;就是让模型在处理每个词时&#xff0c;学会“关注重点”&#xff0c;而不是平均地对每个词一视同仁。这种机制让 GPT 能更聪明地理解句子的上下文和语义之间的关系。 自注意力机制是 GPT 的核心&#xff0c;它帮助模型在理解…

Web 表单开发全解析:从基础到高级掌握 HTML 表单设计

文章目录 前言一、什么是 Web 表单?二、表单元素详解总结前言 在现代 Web 开发中,表单 是用户与后端服务交互的重要桥梁。无论是用户登录、注册、搜索,还是提交反馈,表单都无处不在。在本文中,我们将从基础入手,全面解析表单的核心知识点,并通过示例带你轻松掌握表单开…

HCIE:详解OSPF,从基础到高级特性再到深入研究

目录 前言 一、OSPF协议基本原理 简介 基本原理 OSPF路由器类型 OSPF网络类型 OSPF报文类型和封装 OSPF邻居的建立的维护 DR和BDR的选举 伪节点 LSDB的更新 OSPF的配置 二、OSPF的高级特性 虚连接&#xff08;Virtual-Link&#xff09; OSPF的LSA和路由选择 OSPF…

C#读取本地图像的方法总结

前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任软件经理&#xff0c;从事C#上位机软件开发8年以上&#xff01;我们在C#开发C#程序的时候&#xff0c;有时候需要读取本地图像&#xff0c;下面进行详…

scrapy爬虫框架小案例

豆瓣案例 一、scrapy安装二、scrapy的基本使用&#xff08;爬虫项目创建->爬虫文件创建->运行 爬虫项目结构 response的属性和方法&#x1f31f;&#xff09;1、创建项目2、创建爬虫文件3、scrapy项目的结构4、运行爬虫文件5、response的属性和方法&#xff08;爬虫的处…

服务器实现ssh证书登录

1.生成公钥和私钥 ssh-keygen -t rsa 提示默认生成位置为/root/.ssh/id_rsa ,直接回车。(也可以自己修改) 提示输入证书的密码&#xff0c;可以留空&#xff0c;建议输入&#xff0c;如果输入了&#xff0c;则需要再次确认&#xff0c;记住这个证书密码&#xff08;证书再加…

css:转换

转换 移动 /* transform: translate(100px, 200px); */transform: translateX(100px);transform: translateY(100px); /*一个意思*/ 如果后面跟百分数的意思是移动盒子自身x/y方向长度的百分比&#xff0c;可以用作子绝父相控制盒子水平居中垂直居中 translate里的xy值是相对…

音视频相关的一些基本概念

音视频相关的一些基本概念 文章目录 音视频相关的一些基本概念RTTH264profile & levelI帧 vs IDRMP4 封装格式AAC封装格式TS封装格式Reference RTT TCP中的RTT指的是“往返时延”&#xff08;Round-Trip Time&#xff09;&#xff0c;即从发送方发送数据开始&#xff0c;到…

Flink--API 之Transformation-转换算子的使用解析

目录 一、常用转换算子详解 &#xff08;一&#xff09;map 算子 &#xff08;二&#xff09;flatMap 算子 &#xff08;三&#xff09;filter 算子 &#xff08;四&#xff09;keyBy 算子 元组类型 POJO &#xff08;五&#xff09;reduce 算子 二、合并与连接操作 …

工业公辅车间数智化节能头部企业,蘑菇物联选择 TDengine 升级 AI 云智控

小T导读&#xff1a;在工业节能和智能化转型的浪潮中&#xff0c;蘑菇物联凭借其自研的灵知 AI 大模型走在行业前沿&#xff0c;为高能耗设备和公辅能源车间提供先进的 AI 解决方案。此次采访聚焦于蘑菇物联与 TDengine 的合作项目&#xff0c;通过 AI 云智控平台的建设&#x…

TensorFlow实战:黄文坚版Python代码详解

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;本书由黄文坚撰写&#xff0c;深入介绍了TensorFlow的使用方法。TensorFlow是谷歌开发的开源库&#xff0c;用于数值计算和机器学习&#xff0c;特别是在深度学习方面。书中通过丰富的实例和详细解释&#xff0c…

Java项目中加缓存

Java项目中加缓存 1.更新频率低&#xff1b;但读写频率高的数据很适合加缓存&#xff1b; 2.可以加缓存的地方很多&#xff1a;浏览器的缓存&#xff1b;CDN的缓存&#xff1b;服务器的缓存&#xff1b; 本地内存&#xff1b;分布式远端缓存&#xff1b; 加缓存的时候不要…