环境搭建

安装gradle

  1. 下载Gradle的安装包(可选择二进制包或者完整包
  2. 解压压缩包到安装路径, 如:
    1
    2
    3
    ~$ mkdir -p /usr/local/gradle
    ~$ cd /usr/local/gradle
    ~$ unzip ~/Downloads/gradle-6.0-bin.zip
  3. 导入环境
  • Gradlebin目录添加到PATH环境变量
    1
    2
    ~$ echo "export PATH=$GRADLE_HOME/bin:$PATH" >> ~/.bashrc # ~/.bash_profile ~/.zshrc
    ~$ source ~/.bashrc
  • 或者将 Gradle的可执行文件软连接到 /usr/local/bin
    1
    ~$ ln -s $GRADLE_HOME/bin/gradle /usr/local/bin/gradle

提示: $GRADLE_HOME指的是gradle解压后的目录, 比如 /usr/local/gradle/6.0

创建工程

使用gradle 创建初始工程

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
➜  Kotlin mkdir project
➜ Kotlin cd project
➜ project gradle init
Configuration on demand is an incubating feature.

Select type of project to generate:
1: basic
2: application
3: library
4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
1: C++
2: Groovy
3: Java
4: Kotlin
5: Swift
Enter selection (default: Java) [1..5] 4

Select build script DSL:
1: Groovy
2: Kotlin
Enter selection (default: Kotlin) [1..2] 2

Project name (default: project): app

Source package (default: app): com.oyoung


BUILD SUCCESSFUL in 24s
2 actionable tasks: 2 executed
➜ project

使用IntelliJ Idea打开刚刚创建的项目

在这里插入图片描述

配置工程

修改build.gradle.kts文件

  1. 简化kotlin相关的依赖导入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    dependencies {
    // Align versions of all Kotlin components
    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))

    // Use the Kotlin JDK 8 standard library.
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    // Use the Kotlin test library.
    testImplementation("org.jetbrains.kotlin:kotlin-test")

    // Use the Kotlin JUnit integration.
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
    }
    改为
    1
    2
    3
    4
    5
    6
    7
    8
    dependencies {
    implementation(kotlin("stdlib", "1.3.50"))
    implementation(kotlin("reflect", "1.3.50")) /// kotlin 反射机制需要的库

    /// 暂时不需要改动
    testImplementation("org.jetbrains.kotlin:kotlin-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
    }
  2. 导入springboot 相关插件
    1
    2
    3
    4
    5
    6
    plugins {
    kotlin("jvm") version "1.3.50"
    kotlin("plugin.spring") version "1.3.50"
    id("org.springframework.boot") version "2.2.1.RELEASE"
    id("io.spring.dependency-management") version "1.0.8.RELEASE"
    }
    其中 使用 kotlin()方法包裹的, 属于kotlin模块,使用 id()方法的是第三方模块
  3. 增加 mavenCentral()仓库
    1
    2
    3
    4
    repositories {
    mavenCentral()
    jcenter()
    }
  4. 导入 "org.springframework.boot:spring-boot-starter-web"
    1
    2
    3
    4
    5
    6
    dependencies {
    /// ...
    implementation("org.springframework.boot", "spring-boot-starter-web")
    /// implementation("org.springframework.boot:spring-boot-starter-web")
    /// ...
    }

打开我们的程序入口App.kt

1
2
3
4
5
6
7
8
9
10
11
12
package com.oyoung

class App {
val greeting: String
get() {
return "Hello world."
}
}

fun main(args: Array<String>) {
println(App().greeting)
}

改成如下:

1
2
3
4
5
6
7
8
9
10
package com.oyoung /// 这里改成自己明明的包
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class App

fun main(args: Array<String>) {
runApplication<App>(*args)
}

此刻我们看到runApplication这一行有红色波浪线,鼠标移到上面提示:
错误提示
这表示我们的kotlin build的字节码的jvm版本与我们实际设定的不一致

1
2
3
group = "com.oyoung"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8

按提示增加 -jvm-target 参数

  1. 删除最下面自动生成的 application 任务
  2. 增加一个KotlinCompile任务
    1
    2
    3
    4
    5
    tasks.withType<KotlinCompile>() {
    kotlinOptions {
    jvmTarget = "1.8"
    }
    }
    建议自己敲上面的代码, 在敲代码的过程中会有一个自动导入的import 添加到文件开头
    1
    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    当然了, 如果IDE响应太慢没有自动import,也可以手动导入

首次尝试启动

打开定位为App.kt文件
main 函数的左侧, 显示行号的附近有一个三角形箭头, 点击, 然后会有弹出来的菜单, 之后的操作, 你懂的…
运行

此时, 如果一切正常的话, 将会以默认的8080端口号启动一个没有任何路由请求的SpringBoot应用程序, 如果需要修改为自定义的端口号, 需要在$PROJECT/src/main/resources路径下新建一个application.yml(或者application.properties)文件:

1
2
server:
port: 4000
  • 第二次启动应用时 端口号即可改为自定义的端口号

增加controller

  1. 新建一个 com.oyoung.controller包 (或者使用自己的包前缀)
  2. 新建一个 Controller类, 比如 TestController
  3. 在自定义的Controller上面添加注解 @RestController, 一般会自动导入 所需要的包, 没有自动导入的话, 可以按照提示按 Alt + Enter组合按键手动导入
  4. 添加第一个路由,比如 /test, 返回任意一个对象
  5. 重新启动程序
  6. 如果在上面的依赖中没有添加
    1
    2
    3
    4
    5
    dependencies {
    /// ...
    implementation(kotlin("reflect", "1.3.50"))
    /// ...
    }
    的话, 一般会启动失败, 至于使用什么版本, 看具体情况,我自己选择的是当前最新的稳定版
    至此一个完整的SpringBoot初始项目就创建完成了,

撒花✿✿ヽ(°▽°)ノ✿

从升级macOS升级到10.15(Catalina)以后, 针对屏幕录制增加了权限管理, 所有的会录制屏幕的程序, 在首次开始录制屏幕时, 都会出现请求屏幕录制权限的弹窗

但是升级LICEcap到1.29以后, 点击 record 输入文件名 没带.gif后缀, 因为有文件类型 Gif (*.gif) 然后点 Save, 发现并没有启动屏幕录制, 重试了好几次都无效

这时候去看屏幕录制权限, 发现并没有勾选 LICEcap的选项, 也不能手动添加

于是到网上找前人经验学习, 发现有部分人的屏幕录制权限里面有LICEcap选项可以勾选

所以猜想: LICEcap本身肯定有什么操作是会触发系统弹窗请求屏幕录制权限的

搜罗了网上的各种方法, 大家都说有用的说的最多的是需要执行一条命令

1
~$ xattr -d com.apple.quarantine /Applications/LICEcap.app

我尝试输入以后发现, 问题依旧, 仍是一头雾水

然后试着将LICEcap点击 record 按钮以后的弹窗里面各个选项都分别单独启用一次, 一直都没什么变化

突发奇想,想到把文件名写全 (自己把文件名带上 .gif 后缀, 比如原来一直都用 record_yyyymmdd 这种格式, 然后试着写成 record_yyyymmdd.gif)

示例图

此时奇迹出现, 终于弹出了 LICEcap 请求录制屏幕的警告窗口了, 但是只有拒绝和进入权限设置两个选项

这当然不敢拒绝啊, 点击权限设置后, 发现果然多了一个 LICEcap的勾选处于未选中状态, 而且此时….LICEcap好像已经在录制gif了

LICEcap真NB, 我还没授权呢, 都能直接录制了。。。就因为我 文件多输入了.gif后缀补全了?

此刻表示: 黑人问号尼克杨!!!

然后发现, 没有勾选权限的时候, 虽然能启动录制, 但是只能录制屏幕桌面壁纸

屏幕壁纸动图

勾选了权限以后, 可以录制屏幕上的内容了

屏幕内容动图

不过问题总算解决, 可以继续愉快的使用LICEcap录制我的动图了

问题描述

在使用hexo开发个人博客时, 开启了不蒜子的统计选项, 但是在部署到Github Pages上或者本地使用serve -s 以及python -m SimpleHTTPServer的时候,经常会出现不显示数字问题
由于已经是使用最新的域名来下载的, 应该不存在busuanzi.pure.min.js 的URL不对的情况
使用最新的URL可以直接将脚本下载到本地

解决思路

将busuanzi.pure.min.js下载到本地后,显示这么一大坨

1
var bszCaller,bszTag;!function(){var c,d,e,a=!1,b=[];ready=function(c){return a||"interactive"===document.readyState||"complete"===document.readyState?c.call(document):b.push(function(){return c.call(this)}),this},d=function(){for(var a=0,c=b.length;c>a;a++)b[a].apply(document);b=[]},e=function(){a||(a=!0,d.call(window),document.removeEventListener?document.removeEventListener("DOMContentLoaded",e,!1):document.attachEvent&&(document.detachEvent("onreadystatechange",e),window==window.top&&(clearInterval(c),c=null)))},document.addEventListener?document.addEventListener("DOMContentLoaded",e,!1):document.attachEvent&&(document.attachEvent("onreadystatechange",function(){/loaded|complete/.test(document.readyState)&&e()}),window==window.top&&(c=setInterval(function(){try{a||document.documentElement.doScroll("left")}catch(b){return}e()},5)))}(),bszCaller={fetch:function(a,b){var c="BusuanziCallback_"+Math.floor(1099511627776*Math.random());window[c]=this.evalCall(b),a=a.replace("=BusuanziCallback","="+c),scriptTag=document.createElement("script"),scriptTag.type="text/javascript",scriptTag.defer=!0,scriptTag.src=a,document.getElementsByTagName("head")[0].appendChild(scriptTag)},evalCall:function(a){return function(b){ready(function(){try{a(b); scriptTag.parentElement.removeChild(scriptTag)}catch(c){bszTag.hides()}})}}},bszCaller.fetch("//busuanzi.ibruce.info/busuanzi?jsonpCallback=BusuanziCallback",function(a){bszTag.texts(a),bszTag.shows()}),bszTag={bszs:["site_pv","page_pv","site_uv"],texts:function(a){this.bszs.map(function(b){var c=document.getElementById("busuanzi_value_"+b);c&&(c.innerHTML=a[b])})},hides:function(){this.bszs.map(function(a){var b=document.getElementById("busuanzi_container_"+a);b&&(b.style.display="none")})},shows:function(){this.bszs.map(function(a){var b=document.getElementById("busuanzi_container_"+a);b&&(b.style.display="inline")})}};

经过格式化后:

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
var bszCaller, bszTag;
! function () {
var c, d, e, a = !1,
b = [];
ready = function (c) {
return a || "interactive" === document.readyState || "complete" === document.readyState ? c.call(document) : b.push(function () {
return c.call(this)
}), this
}, d = function () {
for (var a = 0, c = b.length; c > a; a++) b[a].apply(document);
b = []
}, e = function () {
a || (a = !0, d.call(window), document.removeEventListener ? document.removeEventListener("DOMContentLoaded", e, !1) : document.attachEvent && (document.detachEvent("onreadystatechange", e), window == window.top && (clearInterval(c), c = null)))
}, document.addEventListener ? document.addEventListener("DOMContentLoaded", e, !1) : document.attachEvent && (document.attachEvent("onreadystatechange", function () {
/loaded|complete/.test(document.readyState) && e()
}), window == window.top && (c = setInterval(function () {
try {
a || document.documentElement.doScroll("left")
} catch (b) {
return
}
e()
}, 5)))
}(), bszCaller = {
fetch: function (a, b) {
var c = "BusuanziCallback_" + Math.floor(1099511627776 * Math.random());
window[c] = this.evalCall(b), a = a.replace("=BusuanziCallback", "=" + c), scriptTag = document.createElement("script"), scriptTag.type = "text/javascript", scriptTag.defer = !0, scriptTag.src = a, document.getElementsByTagName("head")[0].appendChild(scriptTag)
},
evalCall: function (a) {
return function (b) {
ready(function () {
try {
a(b);
scriptTag.parentElement.removeChild(scriptTag)
} catch (c) {
bszTag.hides()
}
})
}
}
}, bszCaller.fetch("//busuanzi.ibruce.info/busuanzi?jsonpCallback=BusuanziCallback", function (a) {
bszTag.texts(a), bszTag.shows()
}), bszTag = {
bszs: ["site_pv", "page_pv", "site_uv"],
texts: function (a) {
this.bszs.map(function (b) {
var c = document.getElementById("busuanzi_value_" + b);
c && (c.innerHTML = a[b])
})
},
hides: function () {
this.bszs.map(function (a) {
var b = document.getElementById("busuanzi_container_" + a);
b && (b.style.display = "none")
})
},
shows: function () {
this.bszs.map(function (a) {
var b = document.getElementById("busuanzi_container_" + a);
b && (b.style.display = "inline")
})
}
};

于是分析, 其中bszTag起到了控制显示和隐藏功能
搜索 bszTag.hides() 调用
发现了这个部分

1
2
3
4
5
6
try {
a(b);
scriptTag.parentElement.removeChild(scriptTag)
} catch (c) {
bszTag.hides()
}

bszTag.hides() 前加上console日志,

1
2
3
4
5
6
try {
a(b);
scriptTag.parentElement.removeChild(scriptTag)
} catch (c) {
console.error(c), bszTag.hides()
}

然后把js的资源URL替换到站内,再次重新打开页面时, 打印出了报错

call removeChild of null

然后在浏览器打开调试终端, 输入 scriptTag, 打印出了

1
<script type="text/javascript" defer="" src="//busuanzi.ibruce.info/busuanzi?jsonpCallback=BusuanziCallback_95341178475"></script>

再输入 scriptTag.parentElement, 打印出了

1
null

于是确定问题出现在了

1
scriptTag.parentElement.removeChild(scriptTag)

这一行

为了不更改这一行代码的功能, 又要避免出现上面的 removeChild of null 问题

于是修改为

1
2
3
4
5
6
try {
a(b);
if(scriptTag.parentElement) scriptTag.parentElement.removeChild(scriptTag)
} catch (c) {
console.error(c), bszTag.hides()
}

最后重新build并打开本地服务器, 终于显示除了localhost这个使用最广的站点名的访问数字

然后重新压缩脚本, 扔到站内的lib/js目录, 重新build完推送至Github Pages

刷新页面, 终于一切恢复正常

友情链接:

C++网络库cpp-netlib的安装

简单介绍

cpp-netlib是基于asio库的http网络库, 包含了HTTP/HTTPS客户端、服务端的接口支持

cpp-netlib安装位置

头文件一般安装在

1
${CMAKE_INSTALL_PREFIX}/include/boost/network

库文件(.a, .so)一般在

1
${CMAKE_INSTALL_PREFIX}/lib/

其中CMAKE_INSTALL_PREFIX是执行安装脚本时候设定的路径, 比如 CMAKE_INSTALL_PREFIX=/usr/local时, 以上的目录则变为

1
2
/usr/local/include/boost/network
/usr/local/lib

写服务端程序时

需要包含的头文件

1
2
#include <network/uri.hpp>
#include <boost/network/protocol/http/server.hpp>

需要引入的库

1
2
3
4
5
target_link_libraries(app
boost_system
network-uri
cppnetlib-server-parsers
)

其中cppnetlib-server-parsers时服务端解析请求时所需要的库
network-uri是我们自己在解析请求参数的path、query、host、port、protocol等参数需要用到的库
boost_system是cppnetlib-server-parsers以及我们在使用cpp-netlib是调用的一些借口内部依赖的库

示例代码(C++11, cpp-netlib 版本: 0.12.0)

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
#include <network/uri.hpp>
#include <nlohmann/json.hpp>
#include <sspdlog/sspdlog.h>
#include <boost/network/protocol/http/server.hpp>

namespace http = boost::network::http;

using json = nlohmann::json;

struct Controller;

using SimpleHTTPServer = http::server<Controller>;

struct Controller {
void operator() (SimpleHTTPServer::request const &request,
SimpleHTTPServer::connection_ptr response) {
if( request.method == "GET") {
response->set_status(SimpleHTTPServer::connection::ok);
response->set_headers(std::map<std::string, std::string> {
{"Content-Type", "application/json;charset=utf-8"}
});
response->write(json{
{"say", "Who are you?"}
}.dump(2));
}

SSPD_LOG_INFO << "[REQ] Source: " << request.source; /// client info: <ip>:<port>
SSPD_LOG_INFO << "[REQ] Destination: " << request.destination; /// path: /index?name=sb
SSPD_LOG_INFO << "[REQ] Body: " << request.body; /// Body data: {"data": [0, 0]}
SSPD_LOG_INFO << "[REQ] Version: " /// HTTP version
<< static_cast<unsigned>(request.http_version_major)
<< '.'
<< static_cast<unsigned>(request.http_version_minor);

network::uri uri("http://localhost:9090" + request.destination);

SSPD_LOG_INFO << "[URI] Host: " << uri.host();
SSPD_LOG_INFO << "[URI] Port: " << uri.port<unsigned>();
SSPD_LOG_INFO << "[URI] Query: " << uri.query();
SSPD_LOG_INFO << "[URI] Route: " << uri.path();

}

void log(SimpleHTTPServer::string_type const &info) {
std::cerr << "ERROR: " << info << '\n';
}
};

int main(int,char**)
{

Controller control{};
SimpleHTTPServer::options options(control);
SimpleHTTPServer server(options.address("0.0.0.0").port("9090"));
server.run();

return 0;
}

所用到的工具库:
nlohmann/json
sspdlog

开发环境: Ubuntu 16.04

  1. 安装boost
1
~$ sudo aptitude install libboost-all-dev
  1. 安装openssl
1
~$ sudo aptitude install openssl libssl-dev

下载cpp-netlib

1
2
3
~$ mkdir -p workspace
~$ cd workspace
~$ git clone https://github.com/cpp-netlib/cpp-netlib

更新cpp-netlib的三方依赖

1
2
3
~$ cd cpp-netlib
~$ git submodule init
~$ git submodule update

修改 build.sh脚本适合自己的编译(如启动共享库, 启动HTTPS等选项)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/sh
set -e

cmake -Bbuild -H. \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=ON \
-DCPP-NETLIB_ENABLE_HTTPS=ON \
-DCPP-NETLIB_BUILD_EXAMPLES=OFF \
-DCPP-NETLIB_BUILD_DOCS=ON \
-DUri_BUILD_TESTS=OFF \
-DUri_BUILD_DOCS=ON \
-DUri_DISABLE_LIBCXX=ON \
-DCMAKE_CXX_FLAGS="-std=c++11 ${CMAKE_CXX_FLAGS}" \
-DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build

执行build.sh脚本编译

1
~$ ./build.sh

编译没有报错的话, 执行安装(可能要加上 sudo)

1
~ $  cmake --build build --target install

至此cpp-netlib安装完成