上闲鱼拿下了个树莓派4B 8G,300块,我仿佛听见有人说“哇”?哦,其实它的外显输出有点问题,但我是不关心,真香。
到手之后自然是传统装机,点到为止,64位centos安排起来,smb服务给装上,再整个风扇降降温。后续小功能慢慢安排,请勿期待。
进入主题前说下我过的一个小弯。
本来嘛我是装了NextCloud的,这里简单提一句,我在centos7 x86上是能通过自己找httpd/php/mariadb等模块自己一个个装的,但在树莓派上,因为是arm架构,又用了64位,真心不好找资源,所幸有宝塔(戳我),傻瓜安装,更多具体安装步骤我就不找了,因为头发不多了。
但用了几天,发现它对于个人网盘用户来说,尤其是对于我这种脚本抄写大师,简单性能差到令人无法入眠,直接再见了。我的需求很简单,主要是备份照片,需要时备份几个文件而已,在本文后边我会给出我当前的方案。私人网盘存在当然是有道理的,一个是对于搭建是有点困难但对于使用者来说,还是很简单的,另外就是方便权限管理。
先准备好硬件,树莓派4b + 固态硬盘 + 8G+ U盘 + 8G+ TF卡。
然后是操作系统映像,CentOS 8 aarch64系统点我下载,这里就不写怎么烧录了,windows和mac用的工具不一样,都很简单,默认能找centos安装的人都不是小白玩家。
SSD系统安装步骤:戳我
CentOS系统找不到wifi:戳我
nmcli工具的使用:戳我
CentOS8无法直接安装screen:戳我
内网穿透方案:戳我
SMB环境搭建:戳我
我这部分的就是基于windows或mac的自带远程工具实现连接服务器的SMB实现的,所以使用方法就很简单了,在配置smb服务后,windows使用win + r输入//ip:port/path + 账号密码就能登录,mac是在桌面command + k,输入smb://ip:port/path + 账户密码登录。
当然也可以使用SFTP,也就不需要安装SMB服务,我对SMB的需求主要还是远程看漫画(comicscreen只支持smb v1和ftp),之前是放腾讯云上的,确实体验很好。现在嘛还有一个未解决的问题,就是ngrok代理不能直接对SMB进行代理,好像是SMB不仅用了TCP,还用了UDP,所以暂时还没有实现通过本地服务器穿透让ComicScreen看上热乎的漫画。
20201230,已经通过frp看上热乎的漫画了,相对ngrok的方案,缺点就是需要一台有公网的服务器,这点还好,腾讯88一年,还有免费的谷歌羊毛,我都测试过了,腾讯服务器用的重庆的明显比台湾的谷歌延迟低,唉,毕竟是花了钱好。不过说回来,都有公网服务器了,为啥还要内网穿透的哦,这点让我很难受,现在是看在ssh速度明显比ngrok方案好的份儿上,树莓派性能比腾讯那台还是强很多的,只能这样勉强地生活。
奈何NextCloud的速度,上传下载简直让人抓狂,所以最后才有了以下脚本,入图片的脚本基本完善了,放文件的我有时间再写起来,目前没有很大的需求,所以没啥进度。
实现需要使用Termux(未测试iOS系统),这个随便百度下就有下载资源,然后至少需要把文件读取权限给它,配置好SSH免密登录,这里也不表,请自行百度,然后以下分别是手机端和服务器端脚本。
配置好之后,在手机端vim .bashrc,然后加入alias pb='sh yourpath/backupfilename.sh’就可以在打开termux时,输入pb自动完成备份,当然也可以自行研究实现打开termux就自动备份(通过.bashrc或…/usr/etc/profile等实现),也可以实现定时备份等花里胡哨的功能。
手机端
#!/bin/bash
# 默认是DCIM文件夹,这个的具体地址要按自己手机的实际位置来修改
# 可以手动传入指定的其他位置
if [[ ${#} -eq 0 ]]; then
pics_folder="/storage/emulated/0/DCIM/"
elif [[ ${#} -eq 1 ]]; then
pics_folder=${1}
else
echo -e "\033[31m脚本只接受一个文件夹参数\033[0m"
exit 1
fi
# 多个文件传输会重复建立tcp通道,所以先在手机端打包好(未压缩),方便传输
tar_file="/data/data/com.termux/files/home/temp.tar"
tar cf ${tar_file} ${pics_folder}
scp -r ${tar_file} [email protected]:/home/ck/photos/temp/
# 这里使用ssh命令,控制服务器调用python程序进行照片按日期分类
ssh [email protected] "tar xf /home/ck/photos/temp/temp.tar -C /home/ck/photos/temp/; python /root/myProject/myItchat/photo_info_reader/main.py"
rm -f ${tar_file}
服务器端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: CK
# Date: 2020/12/20
import os
import sys
import time
import exifread
abs_path = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.append(os.path.dirname(abs_path))
class PhotoInfoReader:
def __init__(self):
self.pics_path = '/home/ck/photos/'
self.temp_path = '/home/ck/photos/temp/'
os.makedirs(self.temp_path, exist_ok=True)
self.file_suffix = ['ani', 'anim', 'apng', 'art', 'bmp', 'bpg', 'bsave', 'cal', 'cin', 'cpc', 'cpt',
'dds', 'dpx', 'ecw', 'exr', 'fits', 'flic', 'flif', 'fpx', 'gif', 'hdri', 'hevc',
'icer', 'icns', 'ico', 'cur', 'ics', 'ilbm', 'jbig', 'jbig2', 'jng', 'jpg',
'jpeg', 'kra', 'mng', 'miff', 'nrrd', 'ora', 'pam', 'pbm', 'pgm', 'ppm', 'pnm',
'pcx', 'pgf', 'pictor', 'png', 'psd', 'psb', 'psp', 'qtvr', 'ras', 'rbe', 'sgi',
'tga', 'tiff', 'ufo', 'ufp', 'wbmp', 'webp', 'xbm', 'xcf', 'xpm', 'xwd', 'ciff',
'dng', 'ai', 'cdr', 'cgm', 'dxf', 'eva', 'emf', 'gerber', 'hvif', 'iges', 'pgml',
'svg', 'vml', 'wmf', 'xar', 'cdf', 'djvu', 'eps', 'pdf', 'pict', 'ps', 'swf', 'xaml',
'mp4', 'heic']
@staticmethod
def info_reader(pic_path):
"""读取图片信息"""
with open(pic_path, 'rb') as p:
return exifread.process_file(p)
@staticmethod
def move_pic(pic_path, goal_path, color_code):
"""移动图片"""
pic_path = pic_path.replace('(', '\\(').replace(')', '\\)')
os.system('mv -f %s %s' % (pic_path, goal_path))
print('\033[%sm%s\033[0m' % (color_code, pic_path.split('/')[-1]))
@staticmethod
def time_info(key, dic):
"""获取图片时间信息"""
return str(dic[key]).split()[0].split(':')
@staticmethod
def date_confirm(string):
"""从文件名中提取日期信息,只考虑两种情况,太复杂"""
if '_' not in string:
return None
date_string = string.split('_')[1]
if '-' in date_string:
year = date_string.split('-')[0]
month = date_string.split('-')[1]
else:
year = date_string[: 4]
month = date_string[4: 6]
if len(year) == 4 and year[: 2] == '20' and int(month) in range(1, 13):
return year, month
else:
return None
def run(self):
# 判断文件夹下是否包含图片,否则删除,不识别图片格式
# 用于标识文件夹是否存在文件
pin = 0
for top, _, files in os.walk(self.temp_path):
for file in files:
# 排除db文件
if file.split('.')[-1].lower() not in self.file_suffix:
continue
pin = 1
pic_path = os.path.join(top, file)
tags = self.info_reader(pic_path)
# 针对heif格式单独处理
if file.split('.')[-1].lower() == 'heic':
date_info = self.time_info('Image DateTime', tags)
goal_path = os.path.join(self.pics_path, date_info[0], date_info[1])
self.move_pic(pic_path, goal_path, 32)
elif 'EXIF DateTimeOriginal' in tags.keys():
date_info = self.time_info('EXIF DateTimeOriginal', tags)
goal_path = os.path.join(self.pics_path, date_info[0], date_info[1])
self.move_pic(pic_path, goal_path, 32)
else:
# 这里再针对文件名进行处理,看其是否包含日期
date = self.date_confirm(file.split('.')[0])
if date:
year, month = date
goal_path = os.path.join(self.pics_path, year, month)
self.move_pic(pic_path, goal_path, 32)
else:
goal_path = os.path.join('/home/ck/photos/notime')
self.move_pic(pic_path, goal_path, 31)
if not pin:
print('\033[33m未找到待分类文件\033[0m')
# 删除空文件夹
os.system('rm -rf %s; mkdir -p %s' % (self.temp_path, self.temp_path))
def main():
t = PhotoInfoReader()
t.run()
if __name__ == '__main__':
main()
这个函数可以直接添加到.bashrc或.bash_profile中,然后就可以通过put + 文件名把文件丢到服务器上了,注意需要修改自己对应的目录,如果需要新建文件夹,可以自己修改逻辑,判断不存在则mkdir -p即可,再把文件丢到新建的文件夹中。
#!/data/data/com.termux/files/usr/bin/bash
# 这里注意上一行,因为下边语法sh应该是不认的,所以需要指定bash的绝对地址,上述地址就是termux的bash位置
# 其它linux系统bash位置一般是/bin/bash
function put() {
# 得到后缀,建立后缀列表
local file_name=${1}
local suffix=`echo ${file_name##*.}| tr 'A-Z' 'a-z'`
local exe=("exe")
local document=("doc" "xls" "txt" "docx" "xlsx" "ppt" "pptx" "pdf")
local apps=("apk")
local mac=("dmg" "pkg")
local conf=("conf" "xml" "cfg" "bin")
local zip=("zip" "tar" "gz" "xz" "rar" "7z" "tgz")
# 判断文件属于哪类,标记好文件夹名字
if [[ "${exe[*]}" =~ "${suffix}" ]]; then
local folder="exe"
elif [[ "${document[*]}" =~ "${suffix}" ]]; then
local folder="document"
elif [[ "${apps[*]}" =~ "${suffix}" ]]; then
local folder="apps"
elif [[ "${mac[*]}" =~ "${suffix}" ]]; then
local folder="mac"
elif [[ "${conf[*]}" =~ "${suffix}" ]]; then
local folder="conf"
elif [[ "${zip[*]}" =~ "${suffix}" ]]; then
local folder="zip"
else
local folder="other"
fi
# 手动传参
if [[ -n ${2} ]]; then
local folder="${2}"
fi
# 判断文件夹是否存在,不存在则返回报错信息
local final_path="/home/ck/backup/"${folder}
ssh [email protected] "[[ -d ${final_path} ]]"
if [[ ${?} -eq 0 ]]; then
echo -e "\033[32mTargetPath: ${final_path}\033[0m"
scp ${file_name} [email protected]:${final_path}
# echo ${final_path}
else
echo "文件夹不存在"
fi
}
alias put='put ${1} ${2}'
最后是自动温控风扇,需求是随着CPU的温度启停、控制转速,PWM模块我直接淘的,样子见下图。
配套的代码见下,在店家的基础上改了点儿,因为我的小破风扇如果不100%运行,噪音比全开声音更大,只配拥有启停……
#!/usr/bin/env python
# encoding: utf-8
import RPi.GPIO
import time
RPi.GPIO.setwarnings(False)
RPi.GPIO.setmode(RPi.GPIO.BCM)
RPi.GPIO.setup(2, RPi.GPIO.OUT)
pwm = RPi.GPIO.PWM(2, 100)
RPi.GPIO.setwarnings(False)
speed = 0
prv_temp = 0
try:
while True:
tmpFile = open('/sys/class/thermal/thermal_zone0/temp')
cpu_temp = int(tmpFile.read())
tmpFile.close()
if cpu_temp >= 55000:
# if prv_temp < 45000:
# # 启动时防止风扇卡死先全功率转0.1秒
# pwm.start(0)
# pwm.ChangeDutyCycle(100)
# time.sleep(.1)
# speed = min(cpu_temp / 125 - 257, 100)
# 不全速它都有异响,直接干到100就完了
pwm.start(0)
pwm.ChangeDutyCycle(100)
else:
pwm.stop()
prv_temp = cpu_temp
print('\r当前CPU温度:%s' % cpu_temp, end='', flush=True)
time.sleep(30)
except KeyboardInterrupt:
pwm.stop()
参考这个链接中的第2条:树莓派64位CentOS安装