在 CentOS 7 下安装配置 shadowsocks

CentOS 7 开始默认使用Systemd作为开启启动脚本的管理工具,Shadowsocks则是当前比较受欢迎的科学上网工具,本文将介绍如何在 CentOS 下安装和配置 Shadowsocks 服务。


安装 pip

pip是 python 的包管理工具。在本文中将使用 python 版本的 shadowsocks,此版本的 shadowsocks 已发布到 pip 上,因此我们需要通过 pip 命令来安装。

在控制台执行以下命令安装 pip:

1
2
$ curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
$ python get-pip.py

安装配置 shadowsocks


在控制台执行以下命令安装 shadowsocks

1
2
$ pip install --upgrade pip
$ pip install shadowsocks

安装完成后,需要创建配置文件/etc/shadowsocks.json,内容如下:

1
2
3
4
5
6
{
"server": "0.0.0.0",
"server_port": 8388,
"password": "uzon57jd0v869t7w",
"method": "aes-256-cfb"
}

说明:

method为加密方法,可选aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, chacha20, salsa20, rc4, table
server_port为服务监听端口
password为密码,可使用密码生成工具生成一个随机密码
以上三项信息在配置 shadowsocks 客户端时需要配置一致,具体说明可查看 shadowsocks 的帮助文档。

###配置自启动


新建启动脚本文件/etc/systemd/system/shadowsocks.service,内容如下:

1
2
3
4
5
6
7
8
9
[Unit]
Description=Shadowsocks
[Service]
TimeoutStartSec=0
ExecStart=/usr/bin/ssserver -c /etc/shadowsocks.json
[Install]
WantedBy=multi-user.target

执行以下命令启动 shadowsocks 服务:

1
2
$ systemctl enable shadowsocks
$ systemctl start shadowsocks

为了检查 shadowsocks 服务是否已成功启动,可以执行以下命令查看服务的状态:

1
$ systemctl status shadowsocks -l

如果服务启动成功,则控制台显示的信息可能类似这样:

1
2
3
4
5
6
7
8
9
10
11
12
shadowsocks.service - Shadowsocks
Loaded: loaded (/etc/systemd/system/shadowsocks.service; enabled; vendor preset: disabled)
Active: active (running) since Mon 2015-12-21 23:51:48 CST; 11min ago
Main PID: 19334 (ssserver)
CGroup: /system.slice/shadowsocks.service
└─19334 /usr/bin/python /usr/bin/ssserver -c /etc/shadowsocks.json
Dec 21 23:51:48 morning.work systemd[1]: Started Shadowsocks.
Dec 21 23:51:48 morning.work systemd[1]: Starting Shadowsocks...
Dec 21 23:51:48 morning.work ssserver[19334]: INFO: loading config from /etc/shadowsocks.json
Dec 21 23:51:48 morning.work ssserver[19334]: 2015-12-21 23:51:48 INFO loading libcrypto from libcrypto.so.10
Dec 21 23:51:48 morning.work ssserver[19334]: 2015-12-21 23:51:48 INFO starting server at 0.0.0.0:8388

一键安装脚本


新建文件install-shadowsocks.sh,内容如下:

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
#!/bin/bash
# Install Shadowsocks on CentOS 7
echo "Installing Shadowsocks..."
random-string()
{
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w ${1:-32} | head -n 1
}
CONFIG_FILE=/etc/shadowsocks.json
SERVICE_FILE=/etc/systemd/system/shadowsocks.service
SS_PASSWORD=$(random-string 32)
SS_PORT=8388
SS_METHOD=aes-256-cfb
SS_IP=`ip route get 1 | awk '{print $NF;exit}'`
GET_PIP_FILE=/tmp/get-pip.py
# install pip
curl "https://bootstrap.pypa.io/get-pip.py" -o "${GET_PIP_FILE}"
python ${GET_PIP_FILE}
# install shadowsocks
pip install --upgrade pip
pip install shadowsocks
# create shadowsocls config
cat <<EOF | sudo tee ${CONFIG_FILE}
{
"server": "0.0.0.0",
"server_port": ${SS_PORT},
"password": "${SS_PASSWORD}",
"method": "${SS_METHOD}"
}
EOF
# create service
cat <<EOF | sudo tee ${SERVICE_FILE}
[Unit]
Description=Shadowsocks
[Service]
TimeoutStartSec=0
ExecStart=/usr/bin/ssserver -c ${CONFIG_FILE}
[Install]
WantedBy=multi-user.target
EOF
# start service
systemctl enable shadowsocks
systemctl start shadowsocks
# view service status
sleep 5
systemctl status shadowsocks -l
echo "================================"
echo ""
echo "Congratulations! Shadowsocks has been installed on your system."
echo "You shadowsocks connection info:"
echo "--------------------------------"
echo "server: ${SS_IP}"
echo "server_port: ${SS_PORT}"
echo "password: ${SS_PASSWORD}"
echo "method: ${SS_METHOD}"
echo "--------------------------------"

执行以下命令一键安装:

1
2
$ chmod +x install-shadowsocks.sh
$ ./install-shadowsocks.sh

也可以直接执行以下命令从 GitHub 下载安装脚本并执行:

1
$ bash <(curl -s http://morning.work/examples/2015-12/install-shadowsocks.sh)

安装完成后会自动打印出 Shadowsocks 的连接配置信息。比如:

1
2
3
4
5
6
7
8
Congratulations! Shadowsocks has been installed on your system.
You shadowsocks connection info:
--------------------------------
server: 10.0.2.15
server_port: 8388
password: RaskAAcW0IQrVcA7n0QLCEphhng7K4Yc
method: aes-256-cfb
--------------------------------

转自:
http://morning.work/page/2015-12/install-shadowsocks-on-centos-7.html

Centos7服务器安装nginx,mysql,php环境教程

Centos7服务器安装nginx,mysql,php环境教程

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
Centos7版本的linux已经发布到7.0了,本文我们来看看在Centos7的服务器上如何安装nginx+mysql+php环境。
一、概述
项目的需要,今天在虚拟机上基于Centos安装配置了服务器运行环境,web服务用 nginx,数据库存储在mysql(使用MariaDB),动态脚本语言是php。
二、步骤
首页保证Centos7已经安装完毕,正常运行。如果没有安装,请到官网下载(http://www.centos.org/download/ )。如何安装就不在这里赘述了。接下来分5步介绍nginx,mysql,php的安装和配置。
1.第一步:安装nginx
添加centos yum源。
sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
安装nginx
sudo yum install nginx
启动nginx服务
sudo systemctl start nginx.service
访问ip地址,如出现Nginx欢迎页面,则说明nginx已经安装并正常运行。
设置开机自动启动Nginx
sudo systemctl enable nginx.service
2.第二步:安装mysql(用MariaDB)
MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。
安装MariaDB
sudo yum install mariadb-server mariadb
开启MariaDB
sudo systemctl start mariadb
在成功启动 MariaDB/MySQL 服务后,执行在 MariaDB/MySQL 服务包中的脚本。这一次的运行会为为数据库服务器进行一些安全强化措施,如设置(非空)的 root 密码、删除匿名用户、锁定远程访问。
sudo mysql_secure_installation
接下来在命令行会提示设置mysql用户名和密码,全选yes。
设置开机自动重启mysql
sudo systemctl enable mariadb.service
3.第三步,安装PHP
安装Php和php 扩展
sudo yum install php php-mysql php-fpm php-mbstring php-gd php-pear php-mcrypt php-mhash php-eaccelerator php-cli php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc php-mssql php-snmp php-soap php-tidy php-common php-devel php-pecl-xdebug -y
编辑php配置文件
sudo vi /etc/php.ini
cgi.fix_pathinfo=0
设置php-fpm配置文件
sudo vi /etc/php-fpm.d/www.conf
listen = /var/run/php-fpm/php-fpm.sock
启动php-fpm服务
sudo systemctl start php-fpm
设置开机自动重启php-fpm
sudo systemctl enable php-fpm.service
4.第四步:配置nginx站点
编辑站点配置文件
sudo vi /etc/nginx/conf.d/default.conf
server {
listen 80;
server_name drupaluser.org;
root /opt/data;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
重启nginx
sudo systemctl restart nginx
5.第五步:测试php脚本web服务
编辑测试文件
sudo vi /opt/data/info.php
访问页面,能看到php各种配置信息的话说明配置成功。
http://drupaluser.org/info.php
删除测试文件
sudo rm /opt/data/info.php
到此CentOS 7下nginx,mysql,php安装配置全部完成,可以做为web平台的应用环境使用。

转:
http://www.111cn.net/sys/CentOS/87521.htm

免费SSL证书Let's Encrypt(certbot)安装使用

Let’s Encrypt https 证书
使用

##安装方法:
如果是CentOS 6,先执行: yum install epel-release

1
2
3
4
cd /root/
wget https://dl.eff.org/certbot-auto --no-check-certificate
chmod +x ./certbot-auto
./certbot-auto -n

单域名生成证书:

1
2
./certbot-auto certonly --email 邮箱地址 --agree-tos --webroot -w /home/www/网站目录 -d www.chenzhao.date

多域名单目录生成单证书: (即一个网站多个域名使用同一个证书)

1
./certbot-auto certonly --email 邮箱地址 --agree-tos --webroot -w /home/www/网站目录 -d www.chenzhao.date -d chenzhao.date

注: 多个就-d 跟域名 加后面

多域名多目录生成多个证书: (即一次生成多个域名的多个证书)

1
./certbot-auto certonly --email 邮箱地址 --agree-tos --webroot -w /home/www/网站目录 -d www.chenzhao.date -d chenzhao.date -w /home/www/新网站文件地址 -d chenzhao.pro -d www.chenzhao.pro
1
./certbot-auto certonly --email czboosj@gmail.com --agree-tos --webroot -w /var/www/chenzhao.date -d www.chenzhao.date -d chenzhao.date

提示:
IMPORTANT NOTES:

  • Congratulations! Your certificate and chain have been saved at
    /etc/letsencrypt/live/www.chenzhao.date/fullchain.pem. Your cert will
    expire on 2016-10-01. To obtain a new or tweaked version of this
    certificate in the future, simply run certbot-auto again. To
    non-interactively renew all of your certificates, run
    “certbot-auto renew”
  • If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
N多确定啥的一路下来 大致就这些就是ok 的
证书在
/etc/letsencrypt/live/根据你的域名生成的地址/

##证书续期
cerrbot的续期比原来的更加简单,因为证书只有90天,所以建议使用 crontab 进行自动续期:

crontab 里加上如下规则:

1
0 3 */5 * * /root/certbot-auto renew

下面的是在用规则

* */1 * * * /root/certbot-auto renew --quiet --renew-hook "systemctl restart nginx.service"

这样每5天就会执行一次续期操作。当然时间也可以自行进行调整,建议别太频繁,因为他们都有请求次数的限制。

一、Crontab的安装
1、CentOS下面安装Crontab

1
2
3
4
yum install vixie-cron crontabs //安装Crontab
chkconfig crond on //设为开机自启动
service crond start //启动

7 上面

1
2
3
4
1. 查看cron的状态,设为开机启动
$ systemctl status crond (查看状态)
$ systemctl enable crond (设为开机启动)
$ systemctl start crond (启动crond服务)

说明:vixie-cron软件包是cron的主程序;crontabs软件包是用来安装、卸装、 或列举用来驱动 cron 守护进程的表格的程序

二、Crontab使用方法

1、查看crontab定时执行任务列表

crontab -l

2、添加crontab定时执行任务

crontab -e
在里面写上命令
具体命令搜索下以后

我遇到问题是Nginx 证书配置问题

https://mozilla.github.io/server-side-tls/ssl-config-generator/ 一个配置生成地址

生成后主要修改的地方

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 443 ssl http2;
....
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/root_ca_cert_plus_intermediates;
resolver <IP DNS resolver>;
....
}

这6行中,部分文件还不存在,逐个说明。

首先是第一行 listen 443 ssl http2; 作用是启用 Nginx 的 ngxhttpv2_module 模块 支持 HTTP2,Nginx 版本需要高于 1.9.5,且编译时需要设置 –with-http_v2_module 。Arch Linux 的 Nginx 安装包中已经编译了这个模块,可以直接使用。如果你的 Linux 发行版本中的 Nginx 并不支持这个模块,可以自行 Google 如何加上。

ssl_certificate 和 ssl_certificate_key ,分别对应 fullchain.pem 和 privkey.pem,这2个文件是之前就生成好的证书和密钥。

ssl_dhparam 通过下面命令生成:

1
2
$ sudo mkdir /etc/nginx/ssl
$ sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048

(可选 反正我没弄)ssl_trusted_certificate 需要下载 Let’s Encrypt 的 Root Certificates,不过根据 Nginx 官方文档 所说,ssl_certificate 如果已经包含了 intermediates 就不再需要提供 ssltrustedcertificate 了。这一步可以省略:

1
2
3
4
$ cd /etc/letsencrypt/live/example.com
$ sudo wget https://letsencrypt.org/certs/isrgrootx1.pem
$ sudo mv isrgrootx1.pem root.pem
$ sudo cat root.pem chain.pem > root_ca_cert_plus_intermediates

resolver 的作用是 “resolve names of upstream servers into addresses”, 在這個配置中,resolver 是用來解析 OCSP 服務器的域名的,建议填写你的 VPS 提供商的 DNS 服务器,例如我的 VPN 在 Linode,DNS服务器填写:

resolver 106.187.90.5 106.187.93.5;
ip 和分析出来的dns 地址 VPS 服务商的 当时差点忘记
重启nginx
centos 7 重启命令
sudo systemctl restart nginx

在加上上面的自动获取证书应该ok了, 还有待考验

使用 Git Hook 自动部署 Hexo 到个人 VPS

前几个文章安装了git
现在配置下git 实现自动部署
第一步

安装 git:(Ubuntu 14.04)

$ sudo apt-get install git

//yum install git

第二步

创建一个 git 用户,用来运行 git 服务:

$ sudo adduser git
虽说现在的仓库只有我们自己在使用,新建一个 git 用户显得不是很有必要,但是为了安全起见,还是建议使用单独的 git 用户来专门运行 git 服务
设置密码git 用户密码
passwd git xxxx

第三步

创建证书登录,把自己电脑的公钥,也就是 ~/.ssh/id_rsa.pub 文件里的内容添加到服务器的 /home/git/.ssh/authorized_keys 文件中,添加公钥之后可以防止每次 push 都输入密码。
看前面文章配置这个里面的步骤缺失对centos 6 来说配置的不够

第四步

初始化 Git 仓库,我是将其放在 /var/repo/blog.git 目录下的:

1
2
3
$ sudo mkdir /var/repo
$ cd /var/repo
$ sudo git init --bare blog.git

使用 –bare 参数,Git 就会创建一个裸仓库,裸仓库没有工作区,我们不会在裸仓库上进行操作,它只为共享而存在。

第五步

配置 git hooks,关于 hooks 的详情内容可以参考这里。

我们这里要使用的是 post-receive 的 hook,这个 hook 会在整个 git 操作过程完结以后被运行。

在 blog.git/hooks 目录下新建一个 post-receive 文件:

1
2
$ cd /var/repo/blog.git/hooks
$ vim post-receive

在 post-receive 文件中写入如下内容:

1
2
#!/bin/sh
git --work-tree=/var/www/hexo --git-dir=/var/repo/blog.git checkout -f

注意,/var/www/hexo 要换成你自己的部署目录,一般可能都是 /var/www/html。上面那句 git 命令可以在我们每次 push 完之后,把部署目录更新到博客的最新生成状态。这样便可以完成达到自动部署的目的了。

不要忘记设置这个文件的可执行权限:

1
chmod +x post-receive

注: 如果hook 钩子失效, 注意钩子写文件的地方的文件权限
第六步

改变 blog.git 目录的拥有者为 git 用户:

1
$ sudo chown -R git:git blog.git

第七步

禁用 git 用户的 shell 登录权限。

出于安全考虑,我们要让 git 用户不能通过 shell 登录。可以编辑 /etc/passwd 来实现,在 /etc/passwd 中找到类似下面的一行:

git:x:1001:1001:,,,:/home/git:/bin/bash
将其改为:

git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样 git 用户可以通过 ssh 正常使用 git,但是无法登录 sehll

至此,服务器端的配置就完成了。注: 我的配置这里不行

本地配置

配置你的 hexo 博客可以自动 deploy 到服务器上,再也不用 ftp 上传了。

修改 hexo 目录下的 _config.yml 文件,找到 [deploy] 条目,并修改为:

deploy:
type: git
repo: git@www.swiftyper.com:/var/repo/blog.git
branch: master

要注意切换成你自己的服务器地址,以及服务器端 git 仓库的目录。
如果识别不了地址可以用ssh 开头的标准地址, 前面日记也有记录
如:我的配置

1
2
3
4
5
6
7
8
deploy:
type: git
repo:
github: https://github.com/czboosj/czboosj.github.io.git,master
coding: https://git.coding.net/czboosj/czboosj.git,master
104.194.94.204: ssh://git@104.194.94.204:26327/var/repoblog/blog.git
branch: master

本地配置就是如此地简单。至此,我们的 hexo 自动部署已经全部配置好了。

大多数内容转自
http://www.swiftyper.com/2016/04/17/deploy-hexo-with-git-hook/

Centos6 下为git配置SSH验证

server
1.首先安装git

1
yum install git

2.新建一个linux用户,起名为git

1
adduser git

3.在git用户目录中新建目录 .ssh

1
2
cd /home/git/
mkdir .ssh

4.在/home/git/.ssh/目录中新建authorized_keys文件,并将客户端提供的公钥(id_rsa.pub)黏贴到该文件中
touch authorized_keys 或 vi authorized_keys

5.在项目目录创建一个git裸仓库,假如当前项目目录为/home/git/project.git
git init –bare project.git

6.将项目目录和git用户目录下的.ssh目录的所有者和所属组都设置成git

1
2
chown -R git.git project.git
chown -R git.git /home/git/.ssh/

7.为了安全考虑,禁用git用户的shell登录(我这里改了就必须输入密码才行使用,不知道为什么)

1
2
3
vi /etc/passwd
注释 ##git:x:500:500::/home/git:/bin/bash
改为 git:x:500:500:git version control:/home/git:/usr/bin/git-shell

git服务器打开RSA认证

1
2
3
4
5
vi /etc/ssh/sshd_config
下面3个打开
1.RSAAuthentication yes
2.PubkeyAuthentication yes
3.AuthorizedKeysFile .ssh/authorized_keys

client
1.生成公钥,并复制到剪切板

1
2
ssh-keygen -t rsa
pbcopy < ~/.ssh/id_rsa.pub

此步骤要将公钥发给git服务器管理员

2.在本地新建git仓库

1
git init

3.新建一个文件并推送到服务器

1
2
3
4
5
touch readme.txt
git add readme.txt
git commit -m "readme"
git remote add origin git@xxx.xxx.xxx.xxx:/home/git/project.git
git push origin master

注:如果提示需要密码,请检测公钥是否配置成功或RSA是否开启。
转:
https://blog.phpgao.com/build-git-server.html


转得第二个, 用了里面的权限设置
—- 配置SSH Config
$cd /etc/ssh
$vim sshd_config
// 打开以下三个选项
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

—- 生成SSH Key
$ssh-keygen -t rsa -C email@example.com

一路回车,生成文件 id_rsa, id_rsa.pub

—- 创建SSH ras配置文件

// 添加git用户
$sudo adduser git

// 切换到git
$su git

// 进入git主目录,如/home/git
$cd

// 添加.ssh目录、修改权限
// 注:确认.ssh文件夹建立在git的主目录下,如果找不到文件或git权限不够会无法认证。
$mkdir .ssh && chmod 700 .ssh

// 添加授权文件
$touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys

// 把公钥内容复制到授权文件中
$cat id_rsa.pub > .ssh/authorized_keys

—- 服务端创建git bare
$cd /home/git
$mkdir test.git
$cd test.git
$git init –bare

—- 客户端git连接
把私钥id_rsa复制到本地客户端,路径如下:
windows => C:\Users\.ssh\id_rsa

// 启动git shell并进入某个git项目下
// 添加远程url,使用这个方法主要是可能ssh带有非22的默认端口号
git remote set-url origin ssh://git@server:port/home/git/demo.git

// 开始使用git

—- 附:在创建git用户后,为了安全,最好限制git用户不能使用shell

$cat /etc/shells // 看是否已经配置了git-shell,如果没有再添加上去
$which git-shell // 查找git-shell,一般为 /usr/local/bin/git-shell
$vim /etc/shells // 把git-shell路径添加到shells中
$chsh git // 为git用户指定使用的git-shell路径。
$/usr/local/bin/git-shell // 输入git-shell路径

修改了SSH默认端口之后,如何配置git

由于安全或者其它原因,我们可能会修改默认的SSH服务端口号,默认情况下,已有的git项目在pull或者push的时候会报错。

现在假设原来的项目的remote设置为git@domain.com:Projects/p1.git,将服务器SSH默认端口修改为3022后,导致push出错。

有两个解决办法:

一、直接修改URL为SSH://开头

git remote set-url origin ssh://git@domain.com:3022/~/Projects/p1.git
二、 修改本地配置文件

cat>~/.ssh/config

映射一个别名

host newdomain
hostname domain.com
port 3022

ctrl+D

修改p1.git项目下的git配置文件

git remote set-url origin git@newdomain:Projects/p1.git

转自http://zengrong.net/post/1544.htm

如何在CentOS 6.x/7.x上安装git及最新版

记录下远程登录 方法和解说

1
ssh -p 27304 root@104.194.94.204

远程登录 -p 端口 root 用户名 @ 后面是地址

方式一、yum安装

1
# yum install git

通过yum方式安装,版本比较旧,CentOS6.5上安装好是1.7.1版。如果想安装最新版或其他版本,需要使用源码编译安装的方式。

方式二、源码包安装

步骤1. 安装依赖包

1
2
# yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel
# yum install gcc perl-ExtUtils-MakeMaker

步骤2. 卸载旧的git版本(如果之前有安装rpm包)

1
2
# yum remove git

步骤3. 下载&解压

1
2
3
# cd /usr/src
# wget https://www.kernel.org/pub/software/scm/git/git-2.5.0.tar.gz
# tar -zxvf git-2.5.0.tar.gz

或 # wget https://github.com/git/git/archive/v2.5.0.tar.gz 下载

步骤4. 编译安装

1
2
3
4
5
# cd git-2.5.0
# make prefix=/usr/local/git all
# make prefix=/usr/local/git install
# echo "export PATH=$PATH:/usr/local/git/bin" >> /etc/bashrc
# source /etc/bashrc

步骤5. 检查git版本

1
2
# git --version
git version 2.5.0

转载的
https://my.oschina.net/antsky/blog/514586
// 这个说的时git 使用配置自动部署hexo
http://www.swiftyper.com/2016/04/17/deploy-hexo-with-git-hook/

//配置git 后自动部署hexo
http://www.swiftyper.com/2016/04/17/deploy-hexo-with-git-hook/

// nginx 配置
https://gist.github.com/czboosj/cbbf6a44a8bc8a6802ebc7716ca29ec1

http://www.cnblogs.com/liscookie/p/4032928.html

安装完nginx服务器后发现nginx的根目录在/usr/share/nginx/html/下,但是对于部署文件来说,在该目录下是不太习惯的,我就尝试着更改nginx访问的根目录

1
# vi /etc/nginx/conf.d/default.conf
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
#
# The default server
#
server {
listen 80;
server_name localhost;
root /var/www; #修改新的目录为var下的www目录
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /var/www;
index index.html index.htm index.php index.phtml; #添加index.php和index.phtml
# example
#ModSecurityEnabled on;
#ModSecurityConfig /etc/nginx/modsecurity.conf;
}
error_page 404 /404.html;
location = /404.html {
root /var/www; #修改新的目录文件
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www; #修改新的目录文件
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /var/www; #修改新的目录文件
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
参考:http://stackoverflow.com/questions/21820715/how-to-install-latest-version-of-git-on-centos-6-x-7-x

IOS 数据持久化 键盘

先上自己整理的 , 原生得找不到方法就NS了下
1 .plist swift 3

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
/// 写数组到plist 文件
///
/// - Parameters:
/// - fileName: fileName 文件名, 不带.plist 后缀
/// - array: <#array description#>
func saveArrayToPlist(fileName:String,array:[Any]){
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
if path == nil{return}
let fileURLString = path! + "/\(fileName).plist"
(array as NSArray).write(toFile: fileURLString, atomically: true)
}
/// 添加一个数组到 plist 文件 (根据需求,得去掉重复的数据)
///
/// - Parameters:
/// - fileName: <#fileName description#>
/// - array: <#array description#>
func addArrayToPlist(fileName:String,text:String){
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
if path == nil{return}
let fileURLString = path! + "/\(fileName).plist"
var oldArr = self.redArrayToPlist(fileName: fileName)
for i in 0 ..< oldArr.count{
if (oldArr[i] as! String) == text{
oldArr.remove(at: i)
oldArr.insert(text, at: 0)
(oldArr as NSArray).write(toFile: fileURLString, atomically: true)
return
}
}
oldArr.insert(text, at: 0)
(oldArr as NSArray).write(toFile: fileURLString, atomically: true)
}
/// 读取plist 里面的数据Array类型
///
/// - Parameter fileName: <#fileName description#>
/// - Returns: <#return value description#>
func redArrayToPlist(fileName:String)->[Any]{
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
if path == nil{return []}
let fileURLString = path! + "/\(fileName).plist"
let array = NSArray(contentsOfFile: fileURLString)
return array == nil ?[]:array as! [Any]
}
/// 删除写入的数据 写入一个空数据进去
///
/// - Parameter fileName: <#fileName description#>
func removeArrayToPlist(fileName:String){
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
if path == nil{return}
let fileURLString = path! + "/\(fileName).plist"
try? FileManager.default.removeItem(atPath: fileURLString)
// ([] as NSArray).write(toFile: fileURLString, atomically: true)
}

2 配置文件swift3

1
2
3
4
5
6
// 读取
let dic: NSDictionary? = UserDefaults.standard.object(forKey: "UserInfo") as? NSDictionary//读取
// 保存
UserDefaults.standard.set(UserDataDic, forKey: "UserInfo") //
UserDefaults.standard.synchronize()// 立马存入文件

copy 的一些

#概论
所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案:

plist文件(属性列表)
preference(偏好设置)
NSKeyedArchiver(归档)
SQLite 3
CoreData

##沙盒
在介绍各种存储方法之前,有必要说明以下沙盒机制。iOS程序默认情况下只能访问程序自己的目录,这个目录被称为“沙盒”。

###1.结构
既然沙盒就是一个文件夹,那就看看里面有什么吧。沙盒的目录结构如下:

“应用程序包”

1
2
3
4
5
Documents
Library
Caches
Preferences
tmp

###2.目录特性
虽然沙盒中有这么多文件夹,但是没有文件夹都不尽相同,都有各自的特性。所以在选择存放目录时,一定要认真选择适合的目录。

“应用程序包”: 这里面存放的是应用程序的源文件,包括资源文件和可执行文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NSString *path = [[NSBundle mainBundle] bundlePath];
NSLog(@"%@", path);
Documents: 最常用的目录,iTunes同步该应用时会同步此文件夹中的内容,适合存储重要数据。
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSLog(@"%@", path);
Library/Caches: iTunes不会同步此文件夹,适合存储体积大,不需要备份的非重要数据。
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSLog(@"%@", path);
Library/Preferences: iTunes同步该应用时会同步此文件夹中的内容,通常保存应用的设置信息。
tmp: iTunes不会同步此文件夹,系统可能在应用没运行时就删除该目录下的文件,所以此目录适合保存应用中的一些临时文件,用完就删除。
NSString *path = NSTemporaryDirectory();
NSLog(@"%@", path);
plist文件
plist文件是将某些特定的类,通过XML文件的方式保存在目录中。

可以被序列化的类型只有如下几种:

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
NSArray;
NSMutableArray;
NSDictionary;
NSMutableDictionary;
NSData;
NSMutableData;
NSString;
NSMutableString;
NSNumber;
NSDate;
1.获得文件路径
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"];
2.存储
NSArray *array = @[@"123", @"456", @"789"];
[array writeToFile:fileName atomically:YES];
3.读取
NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
NSLog(@"%@", result);

4.注意
只有以上列出的类型才能使用plist文件存储。
存储时使用writeToFile: atomically:方法。 其中atomically表示是否需要先写入一个辅助文件,再把辅助文件拷贝到目标文件地址。这是更安全的写入文件方法,一般都写YES。
读取时使用arrayWithContentsOfFile:方法。
Preference
1.使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1.获得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中写入内容
[userDefaults setObject:@"AAA" forKey:@"a"];
[userDefaults setBool:YES forKey:@"sex"];
[userDefaults setInteger:21 forKey:@"age"];
//2.1立即同步
[userDefaults synchronize];
//3.读取文件
NSString *name = [userDefaults objectForKey:@"a"];
BOOL sex = [userDefaults boolForKey:@"sex"];
NSInteger age = [userDefaults integerForKey:@"age"];
NSLog(@"%@, %d, %ld", name, sex, age);

2.注意
偏好设置是专门用来保存应用程序的配置信息的,一般不要在偏好设置中保存其他数据。
如果没有调用synchronize方法,系统会根据I/O情况不定时刻地保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。
偏好设置会将所有数据保存到同一个文件中。即preference目录下的一个以此应用包名来命名的plist文件。
NSKeyedArchiver
归档在iOS中是另一种形式的序列化,只要遵循了NSCoding协议的对象都可以通过它实现序列化。由于决大多数支持存储数据的Foundation和Cocoa Touch类都遵循了NSCoding协议,因此,对于大多数类来说,归档相对而言还是比较容易实现的。

1.遵循NSCoding协议
NSCoding协议声明了两个方法,这两个方法都是必须实现的。一个用来说明如何将对象编码到归档中,另一个说明如何进行解档来获取一个新对象。

遵循协议和设置属性

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
//1.遵循NSCoding协议
@interface Person : NSObject <NSCoding>
//2.设置属性
@property (strong, nonatomic) UIImage *avatar;
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
@end
实现协议方法
//解档
- (id)initWithCoder:(NSCoder *)aDecoder {
if ([super init]) {
self.avatar = [aDecoder decodeObjectForKey:@"avatar"];
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
//归档
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.avatar forKey:@"avatar"];
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}

特别注意

如果需要归档的类是某个自定义类的子类时,就需要在归档和解档之前先实现父类的归档和解档方法。即 [super encodeWithCoder:aCoder] 和 [super initWithCoder:aDecoder] 方法;

2.使用
需要把对象归档是调用NSKeyedArchiver的工厂方法 archiveRootObject: toFile: 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [[Person alloc] init];
person.avatar = self.avatarView.image;
person.name = self.nameField.text;
person.age = [self.ageField.text integerValue];
[NSKeyedArchiver archiveRootObject:person toFile:file];
需要从文件中解档对象就调用NSKeyedUnarchiver的一个工厂方法 unarchiveObjectWithFile: 即可。
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
if (person) {
self.avatarView.image = person.avatar;
self.nameField.text = person.name;
self.ageField.text = [NSString stringWithFormat:@"%ld", person.age];
}

3.注意
必须遵循并实现NSCoding协议
保存文件的扩展名可以任意指定
继承时必须先调用父类的归档解档方法
SQLite3
之前的所有存储方法,都是覆盖存储。如果想要增加一条数据就必须把整个文件读出来,然后修改数据后再把整个内容覆盖写入文件。所以它们都不适合存储大量的内容。

1.字段类型
表面上SQLite将数据分为以下几种类型:

integer : 整数
real : 实数(浮点数)
text : 文本字符串
blob : 二进制数据,比如文件,图片之类的
实际上SQLite是无类型的。即不管你在创表时指定的字段类型是什么,存储是依然可以存储任意类型的数据。而且在创表时也可以不指定字段类型。SQLite之所以什么类型就是为了良好的编程规范和方便开发人员交流,所以平时在使用时最好设置正确的字段类型!主键必须设置成integer

  1. 准备工作
    准备工作就是导入依赖库啦,在iOS中要使用SQLite3,需要添加库文件:libsqlite3.dylib并导入主头文件,这是一个C语言的库,所以直接使用SQLite3还是比较麻烦的。

3.使用
创建数据库并打开

操作数据库之前必须先指定数据库文件和要操作的表,所以使用SQLite3,首先要打开数据库文件,然后指定或创建一张表。

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
/**
* 打开数据库并创建一个表
*/
- (void)openDatabase {
//1.设置文件名
NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];
//2.打开数据库文件,如果没有会自动创建一个文件
NSInteger result = sqlite3_open(filename.UTF8String, &_sqlite3);
if (result == SQLITE_OK) {
NSLog(@"打开数据库成功!");
//3.创建一个数据库表
char *errmsg = NULL;
sqlite3_exec(_sqlite3, "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)", NULL, NULL, &errmsg);
if (errmsg) {
NSLog(@"错误:%s", errmsg);
} else {
NSLog(@"创表成功!");
}
} else {
NSLog(@"打开数据库失败!");
}
}
执行指令
使用 sqlite3_exec() 方法可以执行任何SQL语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据。
/**
* 往表中插入1000条数据
*/
- (void)insertData {
NSString *nameStr;
NSInteger age;
for (NSInteger i = 0; i < 1000; i++) {
nameStr = [NSString stringWithFormat:@"Bourne-%d", arc4random_uniform(10000)];
age = arc4random_uniform(80) + 20;
NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_person (name, age) VALUES('%@', '%ld')", nameStr, age];
char *errmsg = NULL;
sqlite3_exec(_sqlite3, sql.UTF8String, NULL, NULL, &errmsg);
if (errmsg) {
NSLog(@"错误:%s", errmsg);
}
}
NSLog(@"插入完毕!");
}

查询指令

前面说过一般不使用 sqlite3_exec() 方法查询数据。因为查询数据必须要获得查询结果,所以查询相对比较麻烦。示例代码如下:

sqlite3_prepare_v2() : 检查sql的合法性
sqlite3_step() : 逐行获取查询结果,不断重复,直到最后一条记录
sqlite3_coloum_xxx() : 获取对应类型的内容,iCol对应的就是SQL语句中字段的顺序,从0开始。根据实际查询字段的属性,使用sqlite3_column_xxx取得对应的内容即可。
sqlite3_finalize() : 释放stmt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 从表中读取数据到数组中
*/
- (void)readData {
NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1000];
char *sql = "select name, age from t_person;";
sqlite3_stmt *stmt;
NSInteger result = sqlite3_prepare_v2(_sqlite3, sql, -1, &stmt, NULL);
if (result == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
char *name = (char *)sqlite3_column_text(stmt, 0);
NSInteger age = sqlite3_column_int(stmt, 1);
//创建对象
Person *person = [Person personWithName:[NSString stringWithUTF8String:name] Age:age];
[mArray addObject:person];
}
self.dataList = mArray;
}
sqlite3_finalize(stmt);
}

4.总结
总得来说,SQLite3的使用还是比较麻烦的,因为都是些c语言的函数,理解起来有些困难。不过在一般开发过程中,使用的都是第三方开源库 FMDB,封装了这些基本的c语言方法,使得我们在使用时更加容易理解,提高开发效率。

FMDB
1.简介
FMDB是iOS平台的SQLite数据库框架,它是以OC的方式封装了SQLite的C语言API,它相对于cocoa自带的C语言框架有如下的优点:

使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码
对比苹果自带的Core Data框架,更加轻量级和灵活
提供了多线程安全的数据库操作方法,有效地防止数据混乱
注:FMDB的gitHub地址

2.核心类
FMDB有三个主要的类:

FMDatabase
一个FMDatabase对象就代表一个单独的SQLite数据库,用来执行SQL语句

FMResultSet
使用FMDatabase执行查询后的结果集

FMDatabaseQueue
用于在多线程中执行多个查询或更新,它是线程安全的

3.打开数据库
和c语言框架一样,FMDB通过指定SQLite数据库文件路径来创建FMDatabase对象,但FMDB更加容易理解,使用起来更容易,使用之前一样需要导入sqlite3.dylib。打开数据库方法如下:

1
2
3
4
5
6
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];
FMDatabase *database = [FMDatabase databaseWithPath:path];
if (![database open]) {
NSLog(@"数据库打开失败!");
}

值得注意的是,Path的值可以传入以下三种情况:

具体文件路径,如果不存在会自动创建

空字符串@””,会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,数据库文件也被删除

nil,会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁

4.更新
在FMDB中,除查询以外的所有操作,都称为“更新”, 如:create、drop、insert、update、delete等操作,使用executeUpdate:方法执行更新:

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
//常用方法有以下3种:
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
//示例
[database executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)"];
//或者
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES(?, ?)", @"Bourne", [NSNumber numberWithInt:42]];
5.查询
查询方法也有3种,使用起来相当简单:
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
查询示例:
//1.执行查询
FMResultSet *result = [database executeQuery:@"SELECT * FROM t_person"];
//2.遍历结果集
while ([result next]) {
NSString *name = [result stringForColumn:@"name"];
int age = [result intForColumn:@"age"];
}

6.线程安全
在多个线程中同时使用一个FMDatabase实例是不明智的。不要让多个线程分享同一个FMDatabase实例,它无法在多个线程中同时使用。 如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题。所以,请使用 FMDatabaseQueue,它是线程安全的。以下是使用方法:

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
创建队列。
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
使用队列
[queue inDatabase:^(FMDatabase *database) {
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_1", [NSNumber numberWithInt:1]];
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_2", [NSNumber numberWithInt:2]];
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_3", [NSNumber numberWithInt:3]];
FMResultSet *result = [database executeQuery:@"select * from t_person"];
while([result next]) {
}
}];
而且可以轻松地把简单任务包装到事务里:
[queue inTransaction:^(FMDatabase *database, BOOL *rollback) {
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_1", [NSNumber numberWithInt:1]];
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_2", [NSNumber numberWithInt:2]];
[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_3", [NSNumber numberWithInt:3]];
FMResultSet *result = [database executeQuery:@"select * from t_person"];
while([result next]) {
}
//回滚
*rollback = YES;
}];

FMDatabaseQueue 后台会建立系列化的G-C-D队列,并执行你传给G-C-D队列的块。这意味着 你从多线程同时调用调用方法,GDC也会按它接收的块的顺序来执行。

文/伯恩的遗产(简书作者)
原文链接:http://www.jianshu.com/p/7616cbd72845
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

IOS swift3 键盘

ios swift3 键盘高度获取

1
2
3
4
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
//deinit 的时候remove 下

处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func keyboardWillShow(_ sender:Notification){
// 获取键盘高度
let userInfo = sender.userInfo
if userInfo == nil{
return
}
let value:NSValue = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
let keyBoardRect = value.cgRectValue
let height = keyBoardRect.size.height
}
func keyboardWillHide(_ sender:Notification){
// 隐藏了. 高度自然为0
}

oc 版本

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
- (void)viewDidLoad
{
[super viewDidLoad];
//增加监听,当键盘出现或改变时收出消息
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
//增加监听,当键退出时收出消息
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
//当键盘出现或改变时调用
- (void)keyboardWillShow:(NSNotification *)aNotification
{
//获取键盘的高度
NSDictionary *userInfo = [aNotification userInfo];
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [aValue CGRectValue];
int height = keyboardRect.size.height;
}
//当键退出时调用
- (void)keyboardWillHide:(NSNotification *)aNotification{}

//键盘上面view 自动位置

1
2
NotificationCenter.default.addObserver(self, selector: #selector(self.keybordFrameChange(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keybordHid(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
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
func keybordHid(_ sender:Notification){
var info = (sender as NSNotification).userInfo //as? NSDictionary
let keyboardDuration = (info?[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue ?? 0
self.inputDefaultView.mas_updateConstraints { (make) in
make?.bottom.mas_equalTo()(self.view)
}
UIView.animate(withDuration: keyboardDuration) {
self.view.layoutIfNeeded()
}
}
func keybordFrameChange(_ sender :Notification){
var keyHeight:CGFloat = 246
var info = (sender as NSNotification).userInfo //as? NSDictionary
let keyboardSize = (info?[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue //UIKeyboardFrameEndUserInfoKey UIKeyboardFrameBeginUserInfoKey ,, begin的位置会有差距, 结束的最终的才是ok 的
let keyboardDuration = (info?[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue ?? 0
if keyboardSize != nil{
keyHeight = (keyboardSize!.height)
}
self.inputDefaultView.mas_updateConstraints { (make) in
make?.bottom.mas_equalTo()(self.view)?.offset()(-keyHeight)
}
UIView.animate(withDuration: keyboardDuration) {
self.view.layoutIfNeeded()
}
}

//原本用这个 但是发现有时候位置不对, 换上面的

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
func keyBoardWillShow(_ note:Notification)
{
if (note as NSNotification).userInfo == nil{
return
}
let userInfo = (note as NSNotification).userInfo! as NSDictionary
let keyBoardBounds = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
// let keyBoardBoundsRect = self.view.convertRect(keyBoardBounds, toView:nil)
// let keyBaoardViewFrame = inputeView.frame
let deltaY = keyBoardBounds.size.height
self.inputeDefaultView.isHidden = false
self.view.bringSubview(toFront: self.inputeDefaultView)
let animations:(() -> Void) = {
self.inputeDefaultView.transform = CGAffineTransform(translationX: 0,y: -deltaY)
}
if duration > 0 {
let options = UIViewAnimationOptions(rawValue: UInt((userInfo[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber).intValue << 16))
UIView.animate(withDuration: duration, delay: 0, options:options, animations: animations, completion: nil)
}else{
animations()
}
}
func keyBoardWillHide(_ note:Notification)
{
if (note as NSNotification).userInfo == nil{
return
}
let userInfo = (note as NSNotification).userInfo! as NSDictionary
let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
let animations:(() -> Void) = {
self.inputeDefaultView.transform = CGAffineTransform.identity
}
if duration > 0 {
let options = UIViewAnimationOptions(rawValue: UInt((userInfo[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber).intValue << 16))
UIView.animate(withDuration: duration, delay: 0, options:options, animations: animations, completion: nil)
UIView.animate(withDuration: duration, delay: 0, options: options, animations: animations, completion: { [weak self](bool) in
if let weakSelf = self{
weakSelf.inputeDefaultView.isHidden = true
}
})
}else{
animations()
}
}

hexo 博客使用阿里云oss

今天买了个阿里的oss 研究了下,发现时存储的云, google 了下发现可以用hexo 博客, 但是跳转都是404 不是跳转到.html , 找到hexo git
https://github.com/hexojs/hexo
里面有个前辈已经遇到这个问题
https://github.com/hexojs/hexo/issues/2021

https://github.com/iissnan/hexo-theme-next/issues/604
两个说法, 整理下是, 下载最新版本的hexo, 我的做法是修改hexo 源文件

var suffix = “index.html”; //options.suffix || ‘’;
因为我再主题中调用会失败,特此记录
后来发现绑定域名得备案, 我的域名无法备案, 又买了个能备案的域名发现还得服务号, 得买个云主机,想想放弃了不如买个国外主机了.