Ruby's NoteBook - Ruby
type: channel
link: https://slugyao.top/index.php/author/1/
description: Ruby
items: 文件上传实现断点续传(单线程)
items: 刷题笔记——质数
items: 未选择的路
items: AES算法的C语言实现
items: 排序算法
items: 实习复盘(不完全版)
items: 网站建立一周年
items: 2025阅读记录(下)
items: Typora+Picgo写博客
items: 高考回忆

文件上传实现断点续传(单线程)
type: item
date: 2025-11-28T16:03:49+08:00
description: 在说断点续传之前，先说一下文件上传的原理，因为断点续传的应用场景就是文件上传。文件上传客户端发送上传文件请求服务器收到请求之后解析请求头和请求体，获取上传的数据并进行验证（已经上传的文件不重复上传）服务器端将分片上传的文件存在临时目录中，所有分片上传后再进行合并和重命名最后将文件存储在指定的目录断点续传文件传输过程中，如果出现传输中断或者失败的情况，可以从中断的地方继续传输，不用重新开始传输。断点续传有很多种实现方式，这里推荐b站一个用Java实现多线程断点续传的视频<4.java多线程-实现断点续传_哔哩哔哩_bilibili>在实际应用时我选择用Redis实现断点续传大致的思路：前端计算上传文件的MD5值，如果Redis中由对应的MD5值则说明秒传成功后端通过Redis记录文件分片的情况（Redis中使用Set记录哪些分片已经上传，已经上传过的分片会被跳过），前端开始上传之后，每次上传成功都要记录在Redis中，续传之后前端重新计算MD5值，重新开始上传Redis中记录缺失的分片。当所有分片都上传完成并且合并成文件之后，删除Redis中对应的分片记录。以上思路只适用于单线程，如果要使用多线程方式实现，需要考虑更多的问题~代码实现 @Transactional(rollbackFor = Exception.class) public boolean uploadFile(MultipartFile file, String fileName, String fileMD5, String fileId, HttpServletRequest request, String redisKey) { try { // 参数校验 if (file == null || file.isEmpty()) { throw new IllegalArgumentException("文件不能为空"); } // 如果fileId为空，则生成一个随机的fileId if (StringUtils.isEmpty(fileId)) { fileId = RandomStringUtils.random(10); } // 从请求头中获取分片信息 int chunkNumber = 0; int totalChunks = 1; long chunkSize = file.getSize(); long totalSize = file.getSize(); String chunkMD5 = null; try { chunkNumber = Integer.parseInt(request.getHeader("Chunk-Number")); totalChunks = Integer.parseInt(request.getHeader("Total-Chunks")); chunkSize = Long.parseLong(request.getHeader("Chunk-Size")); totalSize = Long.parseLong(request.getHeader("Total-Size")); chunkMD5 = request.getHeader("Chunk-MD5"); } catch (Exception e) { log.info("未获取到分片信息"); } // 创建临时目录 String tempDir = System.getProperty("java.io.tmpdir") + "/upload/" + fileMD5 + "/"; File tempFileDir = new File(tempDir); if (!tempFileDir.exists()) { tempFileDir.mkdirs(); } // 秒传检查 File finalFile = new File("/final/path/" + fileId + "_" + fileName); if (finalFile.exists() && finalFile.length() == totalSize) { log.info("文件已存在，秒传成功"); return true; } // 处理分片上传 File chunkFile = new File(tempDir + "chunk_" + chunkNumber); // 断点续传检查 - 结合Redis Boolean chunkExistsInRedis = redisTemplate.opsForSet().isMember( String.format(CHUNK_UPLOAD_KEY, fileMD5), String.valueOf(chunkNum…

刷题笔记——质数
type: item
date: 2025-10-31T16:35:17+08:00
description: 简单复习一下有关质数的知识质数的定义在大于1的自然数中，因数只有1和它本身的自然数。由这个定义可以得到，1不是质数，2是最小的质数。判断质数试除法试除法是最快能想到的方法，这里的代码做了一点优化，即从2遍历到sqrt(n)而不是n-1。这是因为：如果n有一个大于sqrt(n)的因子，那么它必定有一个小于sqrt(n)的因子。因此，如果我们在2到sqrt(n)之间没有找到因子，那么大于sqrt(n)的范围内也不会找到因子。在遍历时需要判断i*i<=x，为了防止溢出，这里的写法是i<=x/i#include<iostream> using namespace std; bool is_prime(int x) { if(x<2) return false; else { for(int i=2;i<=x/i;i++)//从2遍历sqrt(x) if(x%i==0) return false; } return true; } int main() { int n,x; cin>>n; int cnt=0; while(n--) { cin>>x; if(is_prime(x)) cnt++; } return cnt; }质数筛质数定理：在1~n之间有n/logn个质数朴素筛法原理：把所有要判断的数列出来，把每一个数的倍数删掉对任意一个数P来说，如果它没有被删掉，则2~P-1都不是它的约数时间复杂度O(nlogn)#include<iostream> #include<algorithm> using namespace std; const int N=1000010; int primes[N],cnt; bool st[N]; void get_primes(int n) { for(int i=2;i<=n;i++) { //如果这个数没有被筛过，说明它是质数 if(!st[i]) { primes[cnt++]=n; } //把每一个数的倍数删掉 for(int j=i+i;j<=n;j+=i) st[j]=true; } } int main() { int n; cin >> n; get_primes(n); cout << cnt << endl; return 0; }埃氏筛法优化：在朴素筛法的基础上，只删除所有质数的倍数//埃氏筛法 #include<iostream> using namespace std; const int N=1000010; int primes[N],cnt; bool st[N]; void get_primes(int n){ for(int i=2;i<=n;i++) { if(!st[i]) { primes[cnt++]=i; for(int j=i*i;j<=n;j+=i) st[j]=true; //可以用质数把所有的合数都筛掉； } } } int main() { int n; cin >> n; get_primes(n); cout << cnt << endl; return 0; }线性筛法原理：从小到大枚举所有质数,筛掉这些质数的倍数对于每一个合数n，只会被它的最小质因子筛掉i % primes[j]==0:primes[j]一定是i的最小质因子，primes[j]也一定是primes[j]*i的最小质因子i % primes[j]≠0:primes[j]一定小于i的所有质因子，primes[j]也一定是primes[j]*i的最小质因子//线性筛法 #include<iostream> using namespace std; const int N=10000010; int primes[N],cnt; bool st[N]; void get_prime(int n) { for(int i=2;i<n;i++) { if(!st[i]) primes[cnt++]=i; for(int j=0;primes[j]<n/i;j++) { st[primes[j]*i]=true; if(i%primes[j]==0)break;//primes[j]一定是i的最小质因子 } } } int main() { int n; cin>>n; get_prime(n); cout<<cnt<<endl; }分解质因数对于一个合数n，把它分解成$p_{1}^{n_{1}}*p_{2}^{n_{2}}$的形式并输出#include<iostream> #include<algorithm> using namespace std; void divide(int n) { for(int i=2;i<=n/i;i++) if(n%i==0) { int …

未选择的路
type: item
date: 2025-09-26T19:16:57+08:00
description: 分享一首从中学时期我就很喜欢的一首诗，希望未来的某一天当我回忆往事时，不会后悔自己所做的选择。未选择的路 弗罗斯特黄色的树林里分出两条路，可惜我不能同时去涉足，我在那路口久久伫立，我向着一条路极目望去，直到它消失在丛林深处。但我却选了另外一条路，它荒草萋萋，十分幽寂，显得更诱人，更美丽；虽然在这条小路上，很少留下旅人的足迹。那天清晨落叶满地，两条路都未经脚印污染。啊，留下一条路等改日再见！但我知道路径延绵无尽头，恐怕我难以再回返。也许多少年后在某个地方，我将轻声叹息将往事回顾：一片树林里分出两条路——而我选择了人迹更少的一条，从此决定了我一生的道路。

AES算法的C语言实现
type: item
date: 2025-09-22T20:17:28+08:00
description: 做了一个小的实践，用C语言实现了128比特版本的AES算法。文章里面有很多公式，我实在调不好LaTex公式的渲染了，如果阅读起来不方便请见谅。1.问题背景及相关知识(1)算法提出背景1976年，“公钥密码”的概念被提出。在此类密码中，公钥用于加密，私钥用于解密，加密解密使用两种不同的密钥。随后1978年发布的基于数论方法构造的RSA算法成为之后几十年密码学的研究方向。针对RSA和DES算法的研究和计算机能力的不断提高，公钥密码体制的安全性逐渐受到威胁。数据加密标准DES不能满足官方和商业应用的安全要求，因此美国国家标准技术研究所NIST征集取代DES的新一代加密标准。经过五年的征集筛选，NIST最终将Rijndael算法确立为新的有效标准，以应对各种针对于RSA，DES算法的先进的密码分析技术，即高级加密标准AES。(2)相关数学知识a.有限域有限域又称为伽瓦罗域，是具有有限个元素的域，集合中元素的个数称为域的阶。M阶域存在当且仅当m是某素数的幂，即存在某个整数$n$和素数$p$，使得$$\begin{align*}\text{m = p}^{\text{n}}\end{align*}$$。$p$称为有限域的特征。b.扩展域如果有限域的阶不是素数，则这样的有限域内的加法和乘法运算就不能用模整数加法和整数乘法模$p$表示。$m>1$的域称为扩展域。在扩展域$$\begin{align*}\text{GF}(2^{\text{m}})\end{align*}$$中元素并不是用整数表示的，而是用系数为$GF(2)$中元素的多项式表示。这个多项式最大的度为$m-1$扩展域内的运算假设$$A(x),B(x) \in \text{GF}(2^{\text{m}})$$扩展域内的加法$$ \begin{align*}C(x) = A(x) + B(x) = \sum_{i=0}^{m-1} c_i x^i \quad \quad c_i \equiv (a_i + b_i) \text{mod} 2\end{align*}$$扩展域内的减法$$\begin{align*}C(x) = A(x) - B(x) = \sum_{i=0}^{m-1} c_i x^i \quad c_i \equiv (a_i - b_i) \text{mod} 2 \equiv (a_i + b_i) \text{mod} 2\end{align*}$$扩展域内的乘法扩展域乘法主要在列混合步骤中使用，我们要将扩展域中的两个元素用多项式形式展开，然后使用多项式乘法规则将两个多项式相乘。$$\begin{align*}C(x) = A(x) \cdot B(x) \text{m} o dP(x)\end{align*}$$通常在多项式乘法中C(x)的度会大于m-1，因此需要对其进行化简。扩展域中进行化简的操作为：将这两个多项式相乘的结果除以一个不可约多项式，最后的结果就是最后的余数。AES算法中使用的不可约多项式为:$$\begin{align*}P(x) = x^8 + x^4 + x^3 + x + 1\end{align*}$$2.算法原理AES算法作为一种对称加密算法，加密/解密时通信双方使用单一密钥。而对称密码算法根据对明文消息加密方式的不同可分为两大类，分组密码和流密码。AES算法属于分组密码算法，它的输入分组、输出分组以及加密/解密过程中的中间分组都是128比特。密钥的长度K可以为128，192或256比特，本次实践中实现的是128比特的版本。AES加密算法的数据处理单元是字节，128比特的分组信息被分成16个字节按顺序被复制到一个的矩阵中，这个矩阵称为状态，AES所有变换都是基于状态的变换。AES的所有变换操作，包括字节替换、行变换、列混合和轮密钥添加，均围绕这个状态矩阵展开。这种矩阵化的表示方式不仅便于数学描述，也有利于硬件实现时的并行处理。轮函数是AES加密的核心组件，通过多轮迭代实现数据的混淆（Confusion）与扩散（Diffusion）。在轮函数的每一轮迭代中包含四步变换，分别是：字节替换，行移位，列混合（最后一轮没有列混合）以及轮密钥添加，其作用即使通过简单的非线性变换、混合函数变换，将字节代换运算产生的非线性扩散达到充分混合，在每轮迭代中引入不同的密钥，这样就能以最简单的运算得到最好的加密效果，实现加密的有效性。AES算法流程图如下：(1)字节替换a. S盒的构造首先对于任意输入的字节求逆元，如果x=0x00，则其逆元定义为0x00，否则在$GF(2^8)$上计算x的逆元。在域上$a \otimes b = 1$$\otimes$表示扩展域内的乘法，则称b为a的逆元对x的逆元应用在$GF(2)$上的仿射变换$\begin{align*…

排序算法
type: item
date: 2025-08-27T09:57:49+08:00
description: 最近写题目的时候发现一些排序算法我不太熟悉，简单记录一下。下面所有的算法都会把数组元素按照升序排序。插入排序插入排序的核心思想是：遍历未排序的序列，对未排序序列的元素插入到已排序的序列中。插入排序的步骤：从第一个元素开始，该元素可以认为已经被排序。取出下一个元素，在已经排序的元素序列中从后向前扫描。如果该元素（已排序）大于新元素，将该元素移到下一位置。重复步骤3，直到找到已排序的元素小于或者等于新元素的位置。将新元素插入到该位置后。重复步骤2~5。public void insertionSort(int[] arr) { if (arr == null || arr.length <= 1) { return; // 数组为空或只有一个元素时无需排序 } for (int i = 1; i < arr.length; i++) { int key = arr[i]; // key是当前需要插入的元素 int j = i - 1; // 将比key大的元素向后移动 while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; // 元素后移 j--; } arr[j + 1] = key; // 把key插入到正确的位置 } }快速排序快速排序使用了“分治”的思想，每次选择一个元素，以这个元素为基准，将数组分为小于等于基准和大于基准的部分。再递归地对两边进行排序（也就是对左右两个区间再此选择基准进行划分，如此重复）。快速排序的步骤：选择基准：从数组中选择一个元素作为基准。基准元素的选择可以是随意的。分区：重新排列数组，使得所有小于基准的元素放在基准前面，大于基准的放在后面。分区结束后，基准处于数组的中间位置。递归：递归地将小于基准的子数组和大于基准的子数组排序。public void quickSort(int[] array) { if (array == null || array.length <= 1) return; sort(array, 0, array.length - 1); } private static void sort(int[] array, int low, int high) { if (low >= high) return; int pivot = array[high]; // 选择最后一个元素作为基准 int i = low - 1; // 较小元素的边界指针 // 分区操作：将小于基准的元素移到左侧 for (int j = low; j < high; j++) { if (array[j] <= pivot) { i++; swap(array, i, j); } } swap(array, i + 1, high); int pivotIndex = i + 1; // 递归排序两边子数组 sort(array, low, pivotIndex - 1); sort(array, pivotIndex + 1, high); } // 交换两个元素的方法 private static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; }归并排序归并排序也用到了“分治”的思想，先将序列分为若干个有序的小区间，最后将这些区间逐步合并。归并排序的步骤：分解：将数组从中间分成两部分。解决：递归地对左右两部分进行归并排序。合并：将两个有序的子数组合并成一个有序数组。 public void mergeSort(int[] array) { if (array == null || array.length <= 1) return; int n = array.length; int[] temp = new int[n]; for (int size = 1; size < n; size *= 2) { // 内层循环处理每个子数组 for (int left = 0; left < n; left += 2 * size) { int mid = Math.min(left + size - 1, n - 1); int right = Math.min(left + 2 * size - 1, n - 1); // 合并操作 int i = left; int j = mid + 1; int k = left; // 比较并合并两个子数组 while (i <= mid && j <= right) { if (array[i] <= array[j]) { temp[k++] = arr…

实习复盘(不完全版)
type: item
date: 2025-08-16T16:51:00+08:00
description: 我在上半年结束了一段实习。这一段实习说长也不长，说短也不短，我对这一段经历也有很多复杂的情感吧😐，所以就复盘一下，可能会有一些我自己总结的经验。起因起因当然是我想给自己的简历加上一段比较好看的实习经历！现在找工作也越来越卷，加上各种网络平台上有各种传播焦虑的帖子，于是，我决定找一份实习工作。前期准备简历首先需要有一份清晰的简历，简历不需要花里胡哨，只需要呈现出最有效的信息(面试官想要看到的信息)，包括但不限于学历信息，求职意向，各种奖项和证书信息。针对每个不同行业，会有一些细化的要求，比如我找的计算机行业的开发工作就要写自己的技术栈。简历的模板有很多，在 各种APP上搜索“求职的方向+简历模板”就能搜到很多示例模板，我使用的是overleaf上的一个免费模板，根据小红书上的攻略自己改的简历，目前来看还有点粗糙。如图，进入overleaf后点击图片上图标上面的Templates（模板）跳转到模板界面搜索简历模板，关键词建议使用“简历/resume”。搜索出的模板都会有提示信息告诉你怎么修改，所以不懂latex语法也不用担心~专业知识专业知识多少要了解一下，面试的时候可能会问相关的名词术语（特别是之前没有实习经历的情况下）。我找的职位是Java后端开发，所以面试之前我去看了看所谓的”八股文“（虽然后面证明我看的这些都没什么用😇）。面试/笔试前期的一些准备工作做完之后，就可以开始寻求实习机会了！我在手机上下载了“海量”求职APP，同时在这么多APP上和一些HR进行沟通。建议在第一次和HR发消息的时候就简要说明自己的求职意向，学历背景，可实习时间等，提高效率。我大概和十几个HR都有沟通，最后是一家公司给我发了笔试的题目，另外一家公司约了线下的面试。这里补充几点判断一个公司是否靠谱的方式：招聘APP里面基本都有公司注册资本，公司人数少于50人，注册资本少于100万的谨慎考虑公司成立时间最好在20年之前，这说明这家公司有一定的抗风险能力招聘岗位信息含糊（没说清楚岗位职责），但是标注的工资却很高的岗位谨慎考虑HR每日和人交流次数很多的，有可能是HR在完成指标，找人意愿可能并不是很强在网络上有各种平台可以查到公司每年缴纳社保的人数，人数很少说明这家公司可能只会给一部分人缴纳社保（这可能同时意味着公司对员工压榨严重）然后就是做笔试题目，然后去线下面试了。笔试的题目对我来说太难了，这里就不说了。线下面试的一些题目倒是可以复盘一下：Java的特性四种类修饰符号的区别如果设计一个系统，有管理员、高级领导、普通用户这三种身份，要建几张表ArrayList与LinkedList的区别常用的linux命令Java创建对象的方式线程中实现同步的关键字wait()和sleep()的区别其中5~8是线下笔试的相关知识点，前面的是面试提的问题。现在想来，我当时的表现可谓是一塌糊涂，能找到实习完全是凭借运气🤨。实习感受面试通过之后一星期我就正式开始实习工作了。因为是实习生，我接触到的基本都是比较成熟的项目。这些项目基本都是使用Vue+Spring boot或Vue+Spring cloud的框架，项目优化过程中要看大量的前端代码，我也不得不去学一些前端的知识。在修复bug的过程中，我觉得最困难的并不是“找到”解决方案，而是理解业务逻辑，然后一边改代码一边和各种各样的小问题斗争😇，同时要考虑到数据库性能，服务器性能。虽然我的工作量不大，但我也算是见过一些奇葩的bug，见过一些稀烂的代码了（变秃了也变强了）。每天做了什么我都会写成一个简单的日记，然后每一周写一次周记，把自己学到的一些处理问题的方法记录一下，方便之后复盘。这是我用obsidian整理的日记和周记网络最后第一次实习，我认识到自己还有很多要学习的，感受到了打工人的疾苦。也意识到有时候自己也不够主动，不会的问题不太愿意主动去问带教，这在职场中可能不太好。总之，我还是觉得这段经历让我成长了很多，即使在这过程中我崩溃了十几次😫。

网站建立一周年
type: item
date: 2025-07-04T19:11:35+08:00
description: 时间过得真快光阴似箭，岁月如梭，一转眼就到了我这个小网站建立一周年的时候了，让我先庆祝一下吧🥳🎉！一年时间里，我在网站上发布了36篇文章，并且给这些文章分好了类。其中“编程”分类下的文章数量最多，浏览量也最大（可能爬虫就喜欢这种关键词的文章🤔）。网站的外观也从一开始较为简单的主题换成了现在这个功能齐全的主题，在折腾网站的路上踩了不少坑，也学到了很多，包括但不限于网站备案，域名绑定，图床设置。虽然这些知识看起来似乎实用性不强，我还是乐在其中，在现在这个时代有一件自己痴迷的事情是一件很珍贵的事。然后感谢我的两位朋友Chailyn's homepage和路红叶先生/YZ | HongYe’s Blogspace，在他们的帮助下我不断完善自己的网站，并坚持写作。他们的网站可以在友链界面点击后跳转。希望这个网站还能坚持运营两年三年甚至更长时间，我会尽力维持现在的更新频率，感谢每一位曾经到访过这个小小网站的朋友🙏。

2025阅读记录(下)
type: item
date: 2025-07-01T08:35:00+08:00
description: 又到了盘点我读了哪些书的时候了😄。上半年有点忙（虽然也不知道在忙些什么），读书的时间短了很多，看的书自然比去年同期少了一些。上半年看的书以小说为主，加上一点点科普类的书籍，我的文学水平不高，自然也没有能力阅读一些著作。2025.1长夜难明这是今年看的第一本书，我是在豆瓣的榜单上看见这本书的评分很高，于是就想读读试试。读完觉得有些失望，小说的文笔感觉很一般，情节感觉也没有很真实。我用中文做了场梦这本书的亮点就是外国人用中文写的，在中国的见闻，有趣的书。2025.2告白最后的反转出乎意料，第一人称的视角来叙述整个故事在推理小说里面还是很少见的。金色梦乡很特别的一本书，说是推理小说好像又不太像，但是情节的设置又很像推理小说。我觉得这本书和《长夜难明》想要表达的中心思想是相似的，不过中日作家的表现方式有所不同，我更喜欢这一本的语言还有情节。13 67准确来说是去年读的书了，但是豆瓣有关这本书的词条老是炸掉😢，今年才标记上。里面有一些反映时代背景的描述，整本书的各个章节之间很连贯，伏笔也很巧妙。2025.3我们为什么要睡觉？看完这本书以后我决定好好睡觉（开个玩笑😊）！说实在的这本书读起来有点乏味，因为里面使用的一些专业术语让人有一种陌生感，加上叙述也是平铺直叙，就是一本普通的科普图书。2025.4龙文身的女孩又是一本北欧的小说，之前也看过一两本北欧作家写的悬疑小说。这些作家的作品总是有一种非常“冷”的恐怖氛围，我猜想或许和北欧的高纬度有关。书中关注了女性受到暴力这一话题，但总觉得写得不够深入。或许是地域文化的差距，北欧作家的文字不像东亚作家那样细腻。2025.5迷人的材料介绍了各种常见的材料，看完并没有喜欢上材料科学😁，或许应该推荐给材料专业的朋友看看。一本通俗易懂的科普书，或许更适合中小学生读🤔。2025.6钟表馆事件本格推理？我不懂各种推理流派的划分。这本书的风格有种“年代感”，作案手法也比较原始而巧妙。我也看了不少推理小说，但始终没能找到符合我的心理预期的“完美”的推理小说。总结一下，上半年看了九本书，基本都是小说。所有书都是看一遍就结束，没有让我印象深刻的书。在这个获取的知识都是高度碎片化的时代，我希望自己依然能有耐心去读完一本几百页的课外书。

Typora+Picgo写博客
type: item
date: 2025-06-21T10:37:00+08:00
description: 这个网站从建立到现在以及快一年了，我也在上面写了三十多篇文章。从最开始直接使用云服务器的磁盘存储博客中的图片，到后面逐渐摸索出用Typora+Picgo+对象存储实现写作的同时便捷地上传博客文章中的图片。下载相关软件typora下载官网指路->Typora 官方中文站typora有一个缺点就是，它现在收费了。PicGo下载官网指路->PicGo购买对象存储因为要把图片传到对象存储中，所以先要购买相关的服务。至于选择买哪一家的服务就全凭个人喜好了，建议选择大厂商的服务防止跑路，一般来说各大厂商的价格之间差别都不是很大，我目前使用的是七牛云的服务。如果是第一次使用对象存储需要对域名进行一些设置，这里就不赘述，详细的教程可以在网上搜索。相关配置首先在PicGo里面进行图床的配置，在PicGo的页面选择你使用的对象存储进行设置，设置里面的Bucket和Key等需要在对象存储厂商设置中获取到，不同厂商的配置略有不同，不太清楚整个流程的话可以网上搜索对应厂商的PicGo配置教程。在typora中配置PicGo的路径打开typora依次点击左上角的“文件”->偏好设置->图像，进入以下界面在右下角上传服务设置里面选中PicGo，并且根据安装的位置设置好PicGo的路径，进行上述设置之后可以点击验证图片上传选项按钮进行验证，验证成功说明上传图片的设置已经设置好了。写作经过以上一通设置之后就可以开始愉快地写博客了！写作的时候可以直接把要插入的图片复制粘贴进来，然后就可以看见图片的本地链接。之后把光标移动到这个链接上面，鼠标右键点击上传图片，图片就会自动上传到配置的对象存储里面。自动上传后会发现链接变成了图床里面的图片链接，但是图片有可能不显示，这时候检查一下图床的图片链接，有可能多了几个字符，比如我在上传图片后，图片的链接会多出“%20”这几个字符，每次上传完毕后手动删除掉这几个字符，图片就能在typora里面显示出来了。例：上传后链接会变为http://%20image.xxx，删除“//”之后的“%20”即可

高考回忆
type: item
date: 2025-06-07T14:57:00+08:00
description: 今天是六月七号，高考的第一天，看着微博热搜榜上被各种有关“高考”的词条占领，我也想回忆一下我的高考，蹭一下高考的热度😝。前一天去看了自己的考场，还意外地看见了一个初三的同班同学。中考后班上选择到市区学校上学的不超过五人，能在此时又见到同班同学，有种说不出的兴奋。六月六号那天晚上还出去吃晚饭了，因为实在受不了食堂了🙃。考前周围的人都挺轻松的，我内心却是紧张与焦虑，两天晚上都是一点多才睡着💤。早上因为要坐公交车去考场，必须起得比较早。那几天的天气并不是很好，有点下雨，而且有些闷。密密的雨总给人淡淡的忧伤之感。关于考试的题目几乎记不清楚了，只剩下大概的印象。第一天是语文和数学，语文的作文题目是《跨越，再跨越》，而且有些题型变化了，没有前两年的题目好做。数学考得偏难，很多个大题我都没有写完，选择题也没有很大把握，从考场出来的时候还看见有人哭了😢，不过我还是尽力使自己保持镇定，因为第二天的理综对我来说也不容易。第一天晚上还有老师来宿舍安慰我们：难度偏大对所有人来说都是一样的，好好复习明天的考试。理综中规中矩，考的时候不觉得很难（相对于数学来说），只是从最后的分数来看有点惨淡😂，英语是最简单最轻松的一门，考完英语就意味着彻底结束了。所有考试都结束后，我坐在公交车上有一种解脱感，这几年的“折磨”终于结束了。有人和老师讨论起了理综的选择题，有人计划着去玩。那一刻，也许大家都真正体会到班主任所说的“世界都在你的脚下”的感觉吧。感叹一句，伴随着高考结束，我的中学生活也这样结束了。那几天就这样过去了，当时看起来特殊的日子，现在再看好像也不过如此。还是很羡慕当时考完解脱的感觉，也许我不会再有类似的感受了，😮‍💨。