smy1999

h0w4b0utW41kT0g3

0%

背景

近期在学习MySQL,完成了Mac端的学习,开始学习服务器部署的进阶内容。起初不打算实际操作了,在本地Mac端实验或者云了内容即可。但仔细想感觉如果没有实地部署操作,经历过程,总是没有什么竞争力,以后实操也会出问题,于是还是打算部署一下。

考虑在宿舍Windows系统安装虚拟机部署,在实验室远程访问宿舍的虚拟机,但是远程桌面的卡顿不能接受,也懒得再弄个路由器解决IP问题,于是这个方式否定了。实验室有好几台服务器,直接在上面装,然后局域网环境内SSH连接,是我想到的很好的解决方法。

因此昨天花了一晚上解决Ubuntu的MySQL8.0安装,也大费周章。

由于需要在宿舍学习,此时无法直接访问局域网内的服务器,考虑解决这个问题。问了实验室的朋友,他不给我解决,但是提供了一些方法。于是我自己搜了一些内网穿透、端口转发的相关内容,需要在服务器端安装工具或者使用成品商业软件,都不是我想要的。此时想到朋友之前让我测试过在宿舍访问他在局域网内树莓派上部署的Blog,是一个不错的方向,之前有印象是他通过路由器转发实现的,于是我也尝试一下。

经过一番折腾和交流,花了大概一个半小时的时间解决了这个问题

路由器端口转发

在路由器管理界面有虚拟服务器的功能,设置服务器端口和IP地址。局域网外访问该路由器的IP地址和服务端口,会被转发到端口转发指定的局域网内IP地址和相同端口

路由器管理界面

原来我以为路由器的服务端口和目标IP地址的端口是不同的,没有找到如何继续修改端口的方式。经过实验,发现在局域网外可以用不指定端口,使用默认端口22访问到朋友的树莓派,而不能指定端口访问到我想要访问到内容。

这里还踩了Ubuntu如何对外开放端口、Ubuntu防火墙的坑。问了朋友才知道,我以为的两个不同端口其实是绑定的,须是同一端口。

因为SSH的默认22端口转发已经被树莓派占用,路由器不能再指定访问服务器的22端口,我想要通过SSH访问服务器,只能是修改服务器上SSH的默认端口,并在端口转发进行设置。

这里走了捷径,直接让朋友把树莓派的SSH端口改了,我就直接用了默认22端口

密钥问题

在局域网外测试访问配置的服务器,遇到了问题

1
2
3
4
5
6
7
8
9
10
11
12
13
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
[...].
Please contact your system administrator.
Add correct host key in */.ssh/known_hosts to get rid of this message.
Offending RSA key in*d/.ssh/known_hosts:86
RSA host key for [...] has changed and you have requested strict checking.
Host key verification failed.

查询和问了朋友发现是因为用这个IP和端口SSH过树莓派,而修改到服务器后,密钥发生了变化,因此删除已有密钥,即可以成功访问服务器。

改进方向

  • 修改服务器SSH默认端口
  • 配置免密登录

后记

写完发现其中技术要点不多,主要还是文字表达-。-当个故事看吧。

最后,感谢小玉

首先的首先,放一下成品

前言

苹果推出的HEIC格式图片,在图像压缩率方面有极大的优势,比JPG格式图片占用空间小、画质更优。上传百度网盘时,保持原有HEIC格式上传,但在Windows系统下载时,仍然是HEIC,但HEIC对Windows支持不佳,因此还是需要转换为传统JPG格式图片。

由于需求是我母亲提出来的,较为硬核向的工具、需要一定代码基础的工具不可行,需要能够直接使用的。为此,我考虑过多种解决方法,但是现有的工具中,都有我不喜欢的部分。我讨厌需要下载的工具,因为它们几乎都含有大量广告,或者大量不需要的冗余功能;在线的转换网站往往上传速度堪忧,并且安全性也得不到保障。

我首先考虑到的解决方式是使用Python或Java开发一个小客户端,因为有这方面的基础,以前也给我母亲写过办公自动化的工具,但是Python和Java的打包较为麻烦,工具的体量也十分巨大。虽然这个问题是可以容忍的问题,但是实际刚着手,便发现 pyheif工具仅支持Mac或Linux操作系统,作者也明确说明了不会支持Windows,因此这种方法也排除了。

后来我又去网上寻找解决方案,比较多的解决方案是基于 heif2any,也提供了相应的在线转换工具,但是示例网站只能转换GIF格式,不能完全满足需求。

我以前没有接触过前端的一切东西,唯一在本科选修过面向包括文体美专业在内的所有人开放的动态网站制作课,现在回看,就是上课做了一个静态网页,用markdown完全可以胜任这个课上的全部内容。由于零基础,不是很确定自己能完成这个工具的开发。

不过解决方案仅此一个,也正好最近对开发感兴趣,同时学学前端的东西,经过一天多的折腾,解决了大大小小的bug,终于是开发完成了。中间很多问题都没有记下来,要么随手解决了,要么尝试了无数种解决方案之后难以静下心来归纳。

本文是这个工具的开发流程,并不全面,仅供参考。

依赖安装

在对应Project的文件夹内安装 heic2any

1
npm install heic2any

自动局部安装于 node_modules 文件夹内,同时生成 package.jsonpackage-lock.json 文件

转换过程

参考https://github.com/alexcorvi/heic2any/blob/master/docs/getting-started.md提供的转换过程

将转换过程写在新的 .js 文件中,在 html 中通过 <script src=""> 的方式引入脚本文件

1
<script type="module" src="./heics2jpgs.js"></script>

使用官方doc的示例代码,以及此文对于Fetch中下载Blob的应用示例,可以运行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fetch(para_path)
.then((res) => res.blob())
.then((blob) =>
heic2any({
blob,
toType: para_format,
quality : para_rate
})
)
.then((conversionResult) => {
// conversionResult is a BLOB
// of the PNG formatted image
let a = document.createElement("a");
a.href = URL.createObjectURL(conversionResult);
a.download = file_name + "." + file_type;
a.style.display = "none";
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(a.href); // 释放URL 对象
a.remove();
})
.catch((e) => {
// see error handling section
console.log(e);
});

经过测试,原图尺寸约为2.8M时,转为JPG后图像尺寸约为18M,这是由于HEIC的先进压缩算法,使得原图可以很小,而转为JPG后压缩算法的压缩率下降,是正常现象。

前端页面

在写好前端页面中的几个选项之后,开始考虑js和HTML的交互,其中遇到了各种问题,包括依赖包格式、找不到对应函数等。

经过各种暴露包、修改路径的尝试,未果,发现在 <script> 中定义 type="module" 后,便无法和 <button>onclick 进行交互了。

同类问题

最终,不适用外部js文件,而是在 html 内部使用,通过 import 的方式引入

1
2
import {convert} from './convert.js';
document.querySelector('#hello').addEventListener('click', convert)

成功引入,能够调用对应 import 的js文件中的方法

数据传递

html 中获取的信息,需要传递到 .js 文件中

前端收集信息的模块有唯一的Attribute id ,因此可以在.js 文件中获取

1
document.getElementById("element_id").value;

根据参考中的内容,这部分好像并不算参数传递,而是直接获取到的

数据处理

传递来的信息有压缩率、图片路径、目标格式,需要对其进行预处理

  • 目标格式

    目标格式在前端已经进行过处理,用户选择后传递的值即为 image/jpg 字符串,无需额外处理

  • 图片路径

    路径传递后,接收到的文件名是正常的,但是路径是有问题的,会有 c:\fakepath\ ,是错误的绝对路径。

    暂时先不处理,使用与 html 同路径的方法

    如果通过

    1
    document.getElementById("files").value;

    则获取的是第一个文件带有 fakepath 的路径,因此需要使用 files 属性获取multiple

  • 压缩率

    压缩率通过 parseFloat() 更改格式

部署

新建一个Git项目 heic2others,增加Page界面

此后就可以通过 smy1999.github.io/heic2others/ 访问了

遇到了GItHub Pages无法读取 node_modules 下js文件的问题,已解决

修改关键流程

上线部署到Github Pages后,发现fetch只能读取url,不能读取本地文件,之前在本地测试的时候,文件也都是在Project的目录中,使用绝对路径之后发现并不能成功读取到文件,于是决定不再使用heic2any提供的流程。

事实上,从 <input type="file" multiple> 读取到的已经是 FileList 格式的文件信息,文件已经被读入前端了,此前的方式多此一举地从文件中提取了文件名,再次使用 Fetch 加载文件。

由于 heic2any 的输入是 blobblob[],起初考虑将 FIle 转换为 blob 输入,经过查询发现, File 已经是 blob 的子类,因此直接将 File 和其他参数传入 heic2any

heic2any 的返回值是 Promise<blob | blob[]> 的格式,通过 .then() 的方式获取其 PromiseResult 属性值,也就是转换后的 blob,将这个 blob 再使用 URL下载。

优化方向

  • 文件批量下载
  • 前端界面优化
  • 稳定性增强

Reference

https://www.mulingyuer.com/archives/553/

https://github.com/alexcorvi/heic2any/blob/master/docs/getting-started.md

值传递:https://stackoverflow.com/questions/21397743/passing-html-input-value-as-a-javascript-function-parameter

创建文章

首先在本地创建 md文件,用于编辑发布的博文内容

  • 方法一

    Blog/source/_posts 目录下创建 md 文件直接编辑

    这种方式需要手动添加头文件,格式为

    1
    2
    3
    4
    5
    ---
    title: <文章标题>
    date: <日期 2022-09-01 16:32:59>
    tags:
    ---
  • 方法二

    Blog 目录中,执行

    1
    hexo new <文件名>

    即可创建对应的 md 文件,同样需要到 Blog/source/_posts 目录下编辑

    使用这种方式生成的 md 文件自动带有头文件,无需自己编辑

发布文章

Blog 目录中,执行

1
hexo d -g

删除文章

进入 Blog/source/_posts路径中,本地删除对应 md文件

执行

1
hexo d -g

完成重新推送,将文章从远程仓库中删除

安装NextT主题

Hexo使用NextT主题

按照上述方法,即可容易地在NextT主题下支持Latex公式。

配置

修改 Blog/theme/next/_config.yml 文件中

1
2
3
4
5
6
7
8
math:
# Default (false) will load mathjax / katex script on demand.
# That is it only render those page which has `mathjax: true` in front-matter.
# If you set it to true, it will load mathjax / katex script EVERY PAGE.
every_page: false

mathjax:
enable: true

enable 设置为 true 即可


对于 every_page 参数,若为 true ,则对所有博文页面渲染Latex;若为 false ,则仅为每个博文 md 文件头部有注释的渲染

例如:

  • 渲染
1
2
3
---
mathjax: true
---
  • 不渲染
1
2
3
---
mathjax: false
---
  • 不渲染
1
2
3
---

---

REF

前言

由于Hexo原生不支持使用Latex语法,因此在研究如何使Hexo支持Latex时遇到了一定的问题

起初尝试了几个支持的引擎,例如 MathJaxhexo-renderer-markdown-it-plus 等。

但是 MathJax 部署之后无法使用,hexo-renderer-markdown-it-plus 遇到了 npm audit相关的问题,这部分问题暂时没能解决,后续如果有机会,回头解决这个问题。

最后,选择使用NextT主题,NextT主题官网有详细的部署流程,使Hexo能够支持Latex。

在Hexo站点的根目录,安装NextT主题

安装

执行如下,即可安装

1
npm install hexo-theme-next

安装完毕后,会生成 Blog/themes/next 目录或 node_modules/hexo-theme-next 目录

如果产生的目录是node_modules/hexo-theme-next

则需要将这个目录复制到 Blog/theme 中,并且将文件夹名命名为 next

配置

Blog/_config.yml 文件中,修改 theme 主题

1
theme: next

同样的方式,也可以修改其他的主题

修改样式

Blog/theme/next/_config.yml 中可以选择不同的Scheme

1
2
3
4
scheme: Muse
scheme: Mist
scheme: Pisces
scheme: Gemini

保留想要的scheme,注释或删除其他的scheme即可

检查

通过本地部署的方式,查看主题修改是否成功

Hexo调试

1
2
3
cd Blog
hexo clean
hexo s --debug

在执行的过程中,命令行会输出NEXT的字样。


注意,此时仍然不支持Latex,需要进一步设置

REF

http://theme-next.iissnan.com/getting-started#activate-next-theme

https://theme-next.js.org/docs/getting-started/

URL: https://matiji.net/exam/brushquestion/583/3846/4C6668FEB8CFD6520DE73B365B31D1A4
题号: MT3583
题库: Matiji

Problem

有$n$个数,给出他们两两的gcd(包括自己和自己)$n^2$个,求这$n$个数。

Input Format

第一行输入一个整数$n(1 \le n \le 500)$

第二行输入$n^2$个整数$a_1\cdots a_{n^2}(1\le a_i \le 5\times 10^4)$

Output Format

打印$n$个整数,输出递增序列

Example Demonstration

Format Example
Input 2
1 1 3 5
Output 3 5

Analysis

最大的数字肯定是$n$个数字中最大的一个,因为要与自身计算GCD的结果就是这个数本身

因此,以此从所有公因数的数组中找到最大的一个数,作为当前找到的最大的数

将当前最大的数与此前找到过的最大的数计算GCD,将公因数数组中排除这些数,表示当前最大数与所有已知数的最大公因数都计算完毕了,剩下的数就是未找到的数之间计算GCD的结果

注意,在计算当前最大数与已知其他数的公因数时,需要计算两次,因为任意一对数之间都计算了两次最大公因数

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import java.util.*;

public class w15p6 {

public static void main(String[] args) {
Scanner input = new Scanner(System.in);
w15p6(input);
}

public static void w15p6(Scanner input) {
// Map用于存放输入数据中不同的数及其出现的次数
Map<Integer, Integer> map = new HashMap<>();

// 输入数据的过程
int n = input.nextInt();
for (int i = 0; i < n * n; i++) {
int in = input.nextInt();
if (map.containsKey(in))
map.put(in, map.get(in) + 1);
else
map.put(in, 1);
}

// result数组用于存放计算过程中的最大公因数
// 由于result数组初始化为0,恰好
int[] result = new int[n];
for (int i = 0; i < n; i++) {
// 从所有公因数中找到最大的作为当前最大公因数,放入result数组中
result[i] = getKey(map);
// 存放后,将这个数移除到所有出现的公因数中,表示移除这个数与自身进行的最大公因数计算结果
// 其他数与这个数的最大公因数计算结果在其他数被找到时计算并移除
// 仅移除1个,如果这个数出现次数为0,则将key从Map中清除
map.put(result[i], map.get(result[i]) - 1);
if (map.get(result[i]) == 0) {
map.remove(result[i]);
}
for (int j = 0; j < i; j++) {
// 遍历所有此前已找到的数
// 计算此前已找到的数与当前找到的数的公因数
int x = gcd(result[i], result[j]);
// 将这个公因数从Map中移除两次,因为一对数之间有两次最大公因数的计算
if (map.containsKey(x)) {
map.put(x, map.get(x) - 1);
if (map.get(x) == 0)
map.remove(x);
}
if (map.containsKey(x)) {
map.put(x, map.get(x) - 1);
if (map.get(x) == 0)
map.remove(x);
}
}
}
// 倒序输出
for (int i = n - 1; i >= 0 ; i--) {
System.out.print(result[i]);
if (i != 0)
System.out.print(" ");
}

}

public static int getKey(Map<Integer, Integer> map) {
// 从存放最大公因数Map中找到最大的公因数,作为当前最大数
Set<Integer> set = map.keySet();
Object arr[] = set.toArray();
Arrays.sort(arr);
return (int)arr[arr.length - 1];
}

public static int gcd(int a,int b) {
return b == 0 ? a : gcd(b, a % b);
}

}