Spring @Component vs @Service vs @Controller vs @Repository

@Component, @Service, @Controller@Repository 四个注解在 Spring 中等同于在XML中定义 <bean> 标签,他们注解的对象都是 Spring 的 Bean。@Service@Controller@Repository 本质上就是 @Component@Controller@Service@Repository 他们在功能上几乎相同,主要的功能是用来给应用分层。

  • @Controller: 处理对应的请求,对应表现层(控制层),使用 @RequestMapping 注解来定义请求 Path,在该层中做请求分发,转发,调用Service方法等
  • @Service: 所有业务逻辑放在 Service 中,对应业务层,包括数值计算,业务逻辑,在该层中直接调用持久层的方法
  • @Repository: 持久层,访问数据,保存数据,DAO(Data Access Objects),比如所有数据库相关的操作
  • @Component: 通用的Spring 组件,generic stereotype for any Spring-managed component,当组件不好归类时可以使用该注解

在配置文件中定义

<context:component-scan base-package="com.mycompany.mypackage" />

Spring 就会自动扫描package下所有类,将带有@Component@Repository@Service@Controller 标签的类自动注册到Spring容器。 简而言之,注解的方式省去了过去需要在 XML 中定义 <bean class="..."> 的繁重工作。component-scan 包含了annotation-config标签的作用。 @Repository@Service@Controller 这三个注解除了作用于不同的软件层面外,其他使用方式和 @Repository 几乎一致。

除了上面的四个注解外,用户同样也可以创建自定义的注解,然后在注解上标注 @Component,那么,该自定义注解便具有了与所 @Component 相同的功能。

当一个 Bean 被自动检测到时,会根据那个扫描器的 BeanNameGenerator 策略生成它的 bean 名称。默认情况下,对于包含 name 属性的 @Component@Repository@Service@Controller,会把 name 取值作为 Bean 的名字。如果这个注解不包含 name 值或是其他被自定义过滤器发现的组件,默认 Bean 名称会是小写开头的非限定类名。如果你不想使用默认 bean 命名策略,可以提供一个自定义的命名策略。首先实现 BeanNameGenerator 接口,确认包含了一个默认的无参数构造方法。然后在配置扫描器时提供一个全限定类名,如下所示:

<beans ...> 
<context:component-scan 
	base-package="a.b" name-generator="a.SimpleNameGenerator"/> 
</beans> 

与通过 XML 配置的 Spring Bean 一样,通过上述注解标识的 Bean,其默认作用域是”singleton”,为了配合这四个注解,在标注 Bean 的同时能够指定 Bean 的作用域,Spring 2.5 引入了 @Scope 注解。 可以在定义Component 的时候指定 @Scope("prototype”) 来改变。

@Component
@Scope("prototype")
public class UserService {
	private int counter;

}

通过名字获取Bean

在一些特殊情况下当我们无法使用注解直接使用 Spring Bean 时,比如在 Filter 中,有一些教程提示我们可以直接使用 ApplicationContext.getBean() 来后去 Bean,但这样的方式不优雅 ,我们可以考虑实现 org.springframework.context.ApplicationContextAware 接口,来动态的根据名字来获取 Bean。文档上

Interface to be implemented by any object that wishes to be notified of the ApplicationContext that it runs in. Implementing this interface makes sense for example when an object requires access to a set of collaborating beans. Note that configuration via bean references is preferable to implementing this interface just for bean lookup purposes.

具体实现如下:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
@Component
public class ContextProvider implements ApplicationContextAware {
 
	private static ApplicationContext CONTEXT;
 
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		CONTEXT = applicationContext;
	}
 
	/**
	 * Get a Spring bean by type.
	 **/
	public static <T> T getBean(Class<T> beanClass) {
		return CONTEXT.getBean(beanClass);
	}
 
	/**
	 * Get a Spring bean by name.
	 **/
	public static Object getBean(String beanName) {
		return CONTEXT.getBean(beanName);
	}
}

上面的类实现,定义了一个简单的 @Component 实现了 ApplicationContextAware 接口,当 Spring Context 被创建时会被通知到。当被通知后 Spring context 会被放到唯一的静态变量 CONTEXT 中,因此静态方法能够通过 getBean 方法找到相应的 Bean 实例。

getBean 的默认名称是类名(头字母小写),如果想简单自定义Bean名字,可以@Service("serviceNewName") 这样来指定。

使用 @PostConstruct 和 @PreDestroy 指定生命周期回调方法

可以使用以下方式指定初始化方法和销毁方法:

@PostConstruct
public void init() { 

} 

@PreDestroy
public void destory() { 

}

Spring Bean 是受 Spring IoC 容器管理,由容器进行初始化和销毁的(prototype 类型由容器初始化之后便不受容器管理),通常我们不需要关注容器对 Bean 的初始化和销毁操作,由 Spring 经过构造函数或者工厂方法创建的 Bean 就是已经初始化完成并立即可用的。然而在某些情况下,可能需要我们手工做一些额外的初始化或者销毁操作,这通常是针对一些资源的获取和释放操作。

Spring 2.5 在保留以上两种方式的基础上,提供了对 JSR-250 的支持。JSR-250 规范定义了两个用于指定声明周期方法的注解:@PostConstruct 和 @PreDestroy。这两个注解使用非常简单,只需分别将他们标注于初始化之后执行的回调方法或者销毁之前执行的回调方法上。由于使用了注解,因此需要配置相应的 Bean 后处理器,亦即在 XML 中增加如下一行:

比较上述三种指定生命周期回调方法的方式,第一种是不建议使用的,不但其用法不如后两种方式灵活,而且无形中增加了代码与框架的耦合度。后面两种方式开发者可以根据使用习惯选择其中一种,但是最好不要混合使用,以免增加维护的难度。

reference


2017-08-13 Spring , Bean , Java , DI , IoC

Java enum 相等比较 == or equal

能否使用 == 来针对 enum 来比较?

答案是:YES, 枚举谨慎的实例化管理允许使用 == 来进行比较,JLS 8.9 Enums 中有Java 语言的规范定义:

枚举类型除了定义时的枚举常量外没有其他实例

如果显示的实例化枚举类型,会产生编译时异常。final clone 方法保证了 Enum 变量不会被 clone, 序列化的机制也保证了重复的实例在反序列化时不会创建额外的枚举变量。通过反射实例化 Enum 类型是被禁止的。所有这四种方式确保了 enum 类型不存在额外的实例,除了定义时的常量

因为每一个枚举常量只有一个实例,因此使用 == 来代替 equals 方法来比较两个枚举是被允许的.

== 和 equals 的区别

使用 == 来代替 equals 也存在两点需要注意的问题。

== 不会抛出空指针异常

enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

不过 == 有编译时类型检查,这一点还是很不错的

enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT);      // DOESN'T COMPILE!!! Incompatible types!

总结

对于不可变类,并且该类能够有效的控制实例数量, == 就是可用的。

在 Effective Java 一书中指出:

考虑静态工厂方法代替构造器,它使得不可变类可以确保不存在两个相等的实例,即当且仅当 a==b 时才有 a.equals(b) 成立,如果类能够保证这一点,它的客户端可以使用 == 来代替 equals 方法,这样可以提升性能,而枚举类型可以保证这一点。

所以在枚举比较中可以使用 ==

  1. 可以正常工作
  2. 更快
  3. 运行时安全
  4. 编译期也是安全的。

reference


2017-08-10 Java , enum

在 Ubuntu 下安装并使用 Cinnamon

Ubuntu 16.04 LTS 或者 Ubuntu 17.04 下可以通过 PPA 来安装 Cinnamon,感谢维护者

命令如下:

sudo add-apt-repository ppa:embrosyn/cinnamon
sudo apt update && sudo apt install cinnamon

当安装完成之后,Log out 或者 重启,在登录界面选择 Cinnamon 来使用。

我在使用一段时间之后才发现没有安装 Nemo 的插件,以至于右击都没有压缩的选项,通过一下步骤安装 Nemo 以及相关套件。

安装 Nemo

sudo add-apt-repository ppa:noobslab/mint
sudo apt-get update
sudo apt-get install nemo

安装插件

sudo apt-get install nemo-compare nemo-dropbox nemo-fileroller nemo-pastebin nemo-seahorse nemo-share nemo-preview nemo-rabbitvcs

安装完成之后退出 nemo :

nemo -q

然后重启 nemo 即可。

reference


2017-08-07 Ubuntu , Linux , Cinnamon , LinuxMint

git common alias

之前有写过 Git alias 的文章, 不过过去很多时间,现在对Git命令越来越熟悉就希望更快的提高输入效率,也越来越感受到 alias 的重要性,不管是直接在bash中的 alias 还是 Git 的alias。所以准备找一些合适的 alias 添加到自己的 gitconfig 文件中长期使用。

添加 alias

使用命令的方式添加

git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.br branch
git config --global alias.hist "log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short"
git config --global alias.type 'cat-file -t'
git config --global alias.dump 'cat-file -p'

git status, git add, git commit, git checkoutgit branch 是最常见的 git 命令,给他们设置 alias 能提高不少效率。使用以上命令添加 alias ,其实作用等同于直接编辑 HOME 目录下的 gitconfig 文件, vim ~/.gitconfig:

[alias]
  co = checkout
  ci = commit
  st = status
  br = branch
  hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short
  type = cat-file -t
  dump = cat-file -p

如果这样设置之后就可以使用 git co <branch> 来切换分支了。

bash alias

可以在 ~/.bashrc 或者 ~/.zshrc 中设置 alias g=git 这样就可以使用 g checkout 来代替 git checkout 了。

reference


2017-08-04

building and distributing python packages with setuptools

If you write something, and you want to share with the world. And let others use through pip install, you can upload your package to pypi.

Create project layout

Put your code in some fold like douban. Write your own setup.py to give basic info about this lib or package. And you can put a README.md file and LICENSE file

douban-dl/
	LICENSE.txt
	README.md
	setup.py
	douban/
		__init__.py
		__main__.py
		other_packages.py

Create setup.py to describe project

setup file often used to describe your project, you can have a template like this:

from setuptools import setup, find_packages 

def readme():
	with open('README.md') as f:
		return f.read()

requirements = [
	"bs4",
	"requests"
]

setup(
	name='douban-dl',
	version='0.0.1',
	description='',
	long_description=readme(),
	packages=['douban',],
	url="https://github.com/einverne/douban-dl",
	author="einverne",
	author_email="einverne@gmail.com",
	entry_points={
		'console_scripts': [
			'douban-dl = douban.__main__:main',
		]},
	keywords="douban downloader",
	packages=find_packages(exclude=["tests"]),
	license='MIT',
    install_requires=requirements,	
)

Create .pypirc file

Register your own account at https://pypi.python.org/pypi. And create following file under ~/.pypirc.

[distutils]
index-servers =
  pypi
  testpypi

[pypi]
username: einverne
password: password

[testpypi]
repository: https://test.pypi.org/legacy
username: einverne
password: password

Upload your first release

Use following command to create a compressed archive file which is under dist sub-directory. This file will wrap-up all project’s source code.

python setup.py sdist

And upload

python setup.py sdist [bdist_wininst] upload

this command upload archive file to pypi. bdist_wininst option alse create windows distribution.

All above had beed test by myself -> https://pypi.python.org/pypi/douban-dl

reference


2017-08-02

Redis 笔记

持久化机制

Redis 支持两种持久化方式,RDB 方式 和 AOF 方式

RDB 方式

RDB 通过快照 snapshotting 完成,也是 Redis 默认的持久化方式,当符合一定条件时 Redis 会自动将内存中的所有数据以快照方式保存一份副本到硬盘上(dump.rdb),这个过程称为”快照“。

Redis 根据以下情况执行快照:

  • 根据配置规则进行自动快照
  • 用户执行 SAVE 或者 BGSAVE 命令
  • 执行 FLUSHALL 命令
  • 执行复制 replication
  1. 配置规则
  • save 900 1 表示在15min(900s) 时间内,有一个或者一个以上键被更改则进行快照。
  • save 300 10 表示 300秒内超过10个key被修改,则发起快照
  1. SAVE 或 BGSAVE 命令

SAVE 命令时, Redis 同步地进行快照操作,会阻塞所有来自客户端的请求。尽量避免在生产环境使用这一命令。

BGSAVE 命令,后台异步进行快照。查看快照是否成功,通过 LASTSAVE 命令获取最近一次成功执行快照时间,返回结果 Unix 时间戳。

  1. FLUSHALL ,Redis 清除数据库所有数据。只要定义了自动快照条件,则会进行快照。如果没有定义自动快照,则不会进行快照。

  2. 复制操作时,即使没有定义自动快照条件,也会生成 RDB 快照

Redis 默认将快照文件存储在工作目录中 dump.rdb 文件中,可以通过配置 dir 和 dbfilename 两个参数分别来指定快照文件的存储路径和文件名。

快照的保存过程:

  1. redis 调用 fork, fork 一份子进程
  2. 父进程处理 client 请求,子进程负责将内存内容写入临时文件,父进程处理写请求时 os 为父进程创建副本,子进程地址空间内的数据是 fork 时刻整个数据库的快照
  3. 当子进程将快照写入临时文件完毕之后,用临时文件替换原来的快照文件,子进程退出。

需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步变更数据。如果数据量大的话,而且写操作比较多, 必然会引起大量的磁盘 io 操作,可能会严重影响性能。

AOF方式

快照的方式是一定间隔备份一次,它的缺点就是如果Redis意外挂掉的话,就会丢失最后一次快照之后的所有修改。如果应用要求不能丢失任何修改可以采用 AOF 持久化方式。 AOF 将 Redis 执行的每一条写命令追加到硬盘文件中(默认为 appendonly.aof)。默认没有开启 AOF (append only file) ,可以通过 appendonly 参数启用:

appendonly yes

AOF 文件保存位置和 RDB 文件位置相同,通过 dir 参数设置,默认为 appendonly.aof ,通过 appendfilename 参数修改:

appendfilename appendonly.aof

Redis 在重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库。

操作系统会在内核中缓存写命令的修改,因此不是立即存盘,所以也可能会导致部分数据丢失。通过 fsync 函数强制系统写入磁盘的时机, 有三种方式如下(默认是:每秒 fsync 一次):

appendonly yes  # 启用 aof 持久化方式
# appendfsync always  # 收到写命令就立即写入磁盘,最慢,但是保证完全的持久化
appendfsync everysec  # 每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中
# appendfsync no  # 完全依赖 os,性能最好,持久化没保证

AOF 方式的缺点:

  • 持久化文件会越来越大,为了解决这个问题 Redis 提供了 bgrewriteaof 命令。 收到此命令 Redis 将使用与快照类似的方式将内存中的数据以命令方式保存到临时文件中,最后替换原来的文件

集群

结构上,容易发生单点故障,分配不同服务器

容量上,内存容易成为存储瓶颈,需要对数据进行分片

集群的特点在于拥有和单机实例同样的性能,同时在网络分区后能够提供一定的可访问性以及对主数据库故障恢复的支持。

主从复制

复制多副本部署不同服务器,防止一台故障丢失数据。通过主从复制可以允许多个 slave server 拥有和 master server 相同的数据库副本。

主从复制特点:

  • master 可以拥有多 slave
  • 多 slave 可以连接到同一个 master 外也能连接到其他 slave
  • 主从复制不会阻塞 master, master 在同步数据时也可以继续处理 client 请求

从数据库配置中:

slaveof 主数据库地址 主数据库端口

当配置好 slave 后,slave 与 master 建立连接,然后发送 sync 命令。无论是第一次连接还是重新连接,master 都会启动一个后台进程,将数据库快照保存到文件中,同时 master 主进程会开始收集新的写命令并缓存。后台进程完成写文件后, master 发送文件给 slave, slave 将文件保存到硬盘上,再加载到内存中,接着 master 就会把缓存的命令转发给 slave,后续 master 将收到的写命令发送给 slave。如果 master 同时收到多个 slave 发来的同步连接命令, master 只会启动一个进程来写数据库镜像,然后发送给所有的 slave。

使用 info 命令来判断当前 redis 是主库还是从库, role 字段,还可以利用 master_link_status 来查看主从是否异步,up 为正常,down 为同步异步。

哨兵

监控 Redis 运行状况。

Redis 命令属性

Redis 不同命令拥有不同的属性,是否只读命令,是否是管理员命令,一个命令可以拥有多个属性。

  • REDIS_CMD_WRITE 属性,会修改 Redis 数据库数据
  • REDIS_CMD_DENYOOM 属性,可能增加 Redis 占用的存储空间,显然拥有该属性的命令都拥有 REDIS_CMD_WRITE 属性。
  • REDIS_CMD_NOSCRIPT 属性,无法在Redis脚本中执行
  • REDIS_CMD_RANDOM 脚本执行了该属性命令之后,不能执行拥有 REDIS_CMD_WRITE 属性命令
  • REDIS_CMD_SORT_FOR_SCRIPT 产生随机结果
  • REDIS_CMD_LOADING 当 Redis 启动时,只会执行拥有该属性的命令

其他

phpRedisAdmin 是一个 PHP 实现的 Redis 管理 Web 界面。地址:https://github.com/erikdubbelboer/phpRedisAdmin

reference


2017-08-01 Redis , Database

openpyxl python lib to read and write excel

Openpyxl 是一个用来处理 Excel 格式文件的 Python 库,它能用来处理 Excel 2007 及以上版本的 excel 文件,也就是 .xlsx/.xlsm/.xltx/.xltm 格式的表格文件。

installation

使用 pip 安装

pip install openpyxl

usage

使用方法包括读和写,参考如下例子:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from openpyxl import Workbook, load_workbook


class ExcelWriter:
	def __init__(self, name):
		self.name = name
		self.wb = Workbook()

	def create_work_sheet(self, sheet_name, index):
		ws = self.wb.create_sheet(sheet_name, index)
		ws.sheet_properties.tabColor = "1072BA"

	def write_row(self):
		ws = self.wb.active
		for row in range(1, 10):  # 1 到 10行
			ws.append(range(10))  # 10 列
		self.wb.save(self.name)

	def set_cell_value(self, row, col, value):
		ws = self.wb.active
		ws.cell(row=row, column=col).value = value

	def save(self):
		self.wb.save(self.name)


class ExcelReader:
	def __init__(self, path):
		self.path = path
		self.wb = load_workbook(path)

	def get_all_sheetnames(self):
		return self.wb.get_sheet_names

	def get_all_rows(self, sheet_name):
		"""
		按行进行迭代
		:param sheet_name:
		:return:
		"""
		ws = self.wb.get_sheet_by_name(sheet_name)  # 调用 get_sheet_by_name 如果 sheet name 不存在返回 None
		if ws is None:
			return
		rows = ws.rows
		for row in rows:
			if row is None:
				return
			yield [col.value for col in row]

	def get_cell_value(self, sheet_name, row, col):
		ws = self.wb.get_sheet_by_name(sheet_name)
		if ws is None:
			return
		return ws.cell(row=row, column=col).value


if __name__ == '__main__':
	writer = ExcelWriter("sample.xlsx")

	writer.write_row()

	reader = ExcelReader('sample.xlsx')
	sn = reader.get_all_sheetnames()

	for data in reader.get_all_rows('Sheet'):
		print data

图片图标

openpyxl 还有很多重量级的功能,比如绘图等等,具体可参考文档。

reference


2017-07-31 Python

raspberry pi 折腾

关于树莓派的装机,配置,系统安装,网络配置等等网上有太多的叫教程,就不在一一介绍。这里主要想要整理一下在折腾过程中遇到的几个问题。一些细节很琐碎,记录下来备忘。我安装的是 Raspberry Pi 官方的系统,也就是 Debian 的衍生系统,所以绝大多数下面的内容在其他 Debian/Ubuntu/Linux Mint 系统上都可以操作。很多内容我在 Mint 下也都已经实现过。

raspberry_pi_2

树莓派的型号

树莓派到今天已经发布了很多代了,当时买的比较早,稍微和新一代比较一下

ras

安装风扇

一图胜过千言

raspberry_pi_fans

安装完成之后

raspberry_pi_fans_install

更新系统

在安装完成之后可以使用国内的 sources.list 源,比如说 清华大学的

deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ jessie main non-free contrib
deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ jessie main non-free contrib

然后更新软件包,一般 update 用来同步本地 package 和 源的 package 索引, update 一定要在 upgrade 或者 dist-upgrade 之前。update 只是用来同步 package 的状态,只是相当于检查更新,而需要手动触发更新。

sudo apt-get update

更新系统, upgrade 用来更新本地安装过的所有 package 的新版本。

sudo apt-get upgrade

更加智能的更新系统,会用更加智能的方式解决包冲突

sudo apt-get dist-upgrade

最后更新 kernel 和 firmware:

sudo rpi-update

中文支持

sudo apt-get instal ttf-wqy-microhei
sudo apt-get install scim-pinyin
sudo dpkg-reconfigure locales

挂载 NTFS

不同系统有不同的文件系统,在 Windows 下绝大部分的文件系统都是 NTFS,当然我一直在用的移动硬盘也格式化成了 NTFS,那么在 Linux 下挂载 NTFS 格式的磁盘,需要借助 ntfs-3g 这个 Package。

NTFS-3G 是微软 NTFS 文件系统的一个开源实现,包括读写支持。

使用如下命令安装 NTFS-3G

sudo apt-get install ntfs-3g

然后使用 sudo fdisk -l 来查看当前系统能够识别的磁盘及分区。一般来说额外的硬盘应该会是类似 /dev/sda1 这样的标示。

使用 mount 命令来挂载 NTFS 磁盘。

sudo mount -t ntfs-3g /dev/sda1 /media/sda1

mount 命令默认会调用 /sbin/mount.ntfs ,它在安装了 ntfs-3g 之后被符号连接到 /bin/ntfs-3g。 确保本地挂载点文件夹存在,否则自己手工新建 mkdir -p /media/sda1

或者可以直接使用 ntfs-3g 命令

ntfs-3g /dev/sda1 /media/sda1

具体参考: Arch Linux Wiki

外网连接

使用 frp , 在外网 VPS 上搞一个 server 。

共享 Samba

在之前的文章 就曾说到使用 Samba 来在局域网共享文件,搭配 Android 上 ES Explorer,电视盒子基本上可以做到类似家庭共享的作用,所有的一切都看路由器能够不能带动了。这里再说一下基本配置。

使用如下命令安装

sudo apt-get install samba

修改配置文件smb.conf

[Public]
   comment = Public Storage  # 共享文件夹说明
   path = /home/pi/Public # 共享文件夹目录
   read only = no # 不只读
   create mask = 0777 # 创建文件的权限
   directory mask = 0777 # 创建文件夹的权限
   guest ok = yes # guest访问,无需密码
   browseable = yes # 可见

设置文件夹权限

sudo chmod -R 777 /home/pi/Public/

重启 Samba 服务

sudo samba restart

完成后局域网中的其他设备就可以通过,\\IP\ 来访问共享的内容

安装 Resilio Sync

很早之前,我也写过 Resilio Sync 的文章,那个时候还叫 BTSync。在笔记本上一直跑着,这些天我看树莓派负载也不高,跑一个 Resilio Sync 应该也还可以。

不过过程却有点繁复,国内似乎屏蔽了 Resilio Sync 的官网,连他的 Key 都无法下载下来,可以使用 proxychains 代理的方法(可以参考之前的文章),不过毕竟也稍微麻烦一点,不过后来发现,在官网下载一个可运行的 rslsync ,再配置一些 conf 文件就可以直接开跑。

在文件 /etc/apt/sources.list.d/resilio-sync.list 中写入:

deb [arch=armhf] http://linux-packages.resilio.com/resilio-sync/deb resilio-sync non-free

添加公钥

wget -qO - https://linux-packages.resilio.com/resilio-sync/key.asc | sudo apt-key add -

更新 index

sudo dpkg --add-architecture armhf
sudo apt-get update

安装

sudo apt-get install resilio-sync

安装完之后,如果想要修改 conf 文件,可以去相应的位置 /etc 下找,然后使用树莓派的IP:8888 来访问WEB 管理界面。

然后就是添加相应的 KEY 来同步文件了。具体可以参考我之前那篇文章。

raspi_2_b

reference


2017-07-29 RaspberryPi , Linux

flask admin

Flask Admin 是 Flask 的一个管理插件,类似于 Django Admin 一样的存在,可以很方便的帮助开发者实现管理界面,并且能够提供一套和数据库对应的界面。

Flask-Admin提供一个现成的SQLAlchemy模型接口。它以类执行并接受2个参数:模型类和数据库会话。

初始化 Flask Admin

from flask import Flask
from flask.ext.admin import Admin

app = Flask(__name__)

admin = Admin(app) # Add administrative views here

app.run()

运行这个程序并访问 http://localhost:5000/admin/ ,可以看到基础模板。

增加一个管理视图

from flask import Flask
from flask.ext.admin import Admin, BaseView, expose

class MyView(BaseView):
	@expose('/')
	def index(self):
		return self.render('index.html')

app = Flask(__name__)

admin = Admin(app)
admin.add_view(MyView(name='Hello'))

app.run()

模型视图

模型视图允许你为数据库中的每个模型增加专用的管理页面。可以自动为数据库中每一张表生成一个管理页面。

from flask.ext.admin.contrib.sqla import ModelView

Flask and Flask-SQLAlchemy initialization here

admin = Admin(app)
admin.add_view(ModelView(User, db.session))

构造自己的 ModelView,要定制这些模型视图,有两个选择:一是覆盖 ModelView 类的公有属性,二是覆盖它的方法。

from flask.ext.admin.contrib.sqla import ModelView

# Flask and Flask-SQLAlchemy initialization here

class MyView(ModelView):
	# Disable model creation
	can_create = False

	# Override displayed fields
	column_list = ('login', 'email')

	def __init__(self, session, **kwargs):
		# You can pass name and other parameters if you want to
		super(MyView, self).__init__(User, session, **kwargs)

admin = Admin(app)
admin.add_view(MyView(db.session))

文件管理

from flask.ext.admin.contrib.fileadmin import FileAdmin

import os.path as op

# Flask setup here

admin = Admin(app)

path = op.join(op.dirname(__file__), 'static')
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))

2017-07-28 Flask , Web , Python , Linux

flask upload files

上传的文件存放在 static/uploads 目录中。

werkzeug 库可以判断文件名是否安全,例如防止文件名是 /../test.png, 安装

pip install werkzeug

具体代码:

from flask import Flask, request
from werkzeug.utils import secure_filename
import os
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/uploads/'
app.config['ALLOWED_EXTENSIONS'] = set(['png', 'jpg', 'jpeg', 'gif']) # For a given file, return whether it's an allowed type or not
def allowed_file(filename):
	return '.' in filename and \
		   filename.rsplit('.', 1)[1] in app.config['ALLOWED_EXTENSIONS']

@app.route('/')
def hello_world():
	return 'hello world'

@app.route('/upload', methods=['POST'])
def upload():
	upload_file = request.files['file']
	if upload_file and allowed_file(upload_file.filename):
		filename = secure_filename(upload_file.filename)
		upload_file.save(os.path.join(app.root_path, app.config['UPLOAD_FOLDER'], filename))
		return 'hello, '+request.form.get('name', 'little apple')+'. success'
	else:
		return 'hello, '+request.form.get('name', 'little apple')+'. failed'

if __name__ == '__main__':
	app.run(debug=True)

app.config中的config是字典的子类,可以用来设置自有的配置信息,也可以设置自己的配置信息。函数 allowed_file(filename) 用来判断filename是否有后缀以及后缀是否在app.config[‘ALLOWED_EXTENSIONS’]中。

模拟上传文件

import requests
files = {'file': open('01.jpg', 'rb')}
user_info = {'name': 'letian'}
r = requests.post("http://127.0.0.1:5000/upload", data=user_info, files=files)
print r.text

要控制上产文件的大小,可以设置请求实体的大小,例如:

app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 #16MB

不过,在处理上传文件时候,需要使用try:…except:…。

如果要获取上传文件的内容可以:

file_content = request.files['image01'].stream.read()

使用template

在 template 目录下新建 upload.html

<form action="" method=post enctype=multipart/form-data>
	<div class="form-group">
	  <label for="exampleInputFile">上传文件</label>
	  <input type="file" name="file">
	  <p class="help-block">上传数据</p>
	  <button type="submit" class="btn btn-default">Submit</button>
	</div>
</form>

在 views.py 中写 upload_file 方法处理上传逻辑

@crawler_control.route('/upload', methods=['POST'])
def upload_file():
	if request.method == 'POST':
		f = request.files['file']
		if f and '.' in f.filename and f.filename.rsplit('.', 1)[1] in app.config['ALLOWED_EXTENSIONS']:
			filename = secure_filename(f.filename)
			path = op.join('/tmp', filename)
			f.save(path)
		return "Success"
	return render_template_string("only support POST")

2017-07-27 Flask , Web , Python , Linux

Google+

最近文章

  • 又一款抓包分析软件 wireshark Wireshark 是一款网络分析工具,也是学习网络协议的工具,原先介绍过的 Charles ,mitmproxy 等HTTP抓包工具,都局限于 HTTP/HTTPS 请求,对于更底层的 TCP/IP,UDP 等协议就无能为力了。Wireshark 可以抓取网卡上的网络包,并实时展示,Wireshark 包括了过滤器,协议显示等等工具。
  • 关于游戏的一些想法 最近国内又火了一个答题游戏,回答正确12题平分多少多少万奖金。先不说他们都抄袭 HQ Trivia 这款App,但我这两天一直在想一个问题,为什么这一类的应用能火起来,难道就是因为最后平分100万,200万,现金带来的刺激吗?我想答案一定不是的,除了最开始的玩家或许还能分到几十块钱,在入场玩家越来越多的情况下,每个人瓜分到的奖励一定是越来越少的。那到底是什么能让一个应用一夜之间火到大江南北?
  • Android 电视盒子可用的应用备份 用盒子也已经很多年了,几年来家里,自己用,也积累了一些常用的应用。这两天又拿到了 T1 盒子,又才想起来整理这样一份单子,这样不用每一次都一遍一遍的尝试了。记得以前 VST ,泰捷视频都还很不错的时候,再后来广电发了禁令,再后来这片市场混乱发展,各家大型网站优酷,爱奇艺又不敢公开大搞,却又在背后偷偷摸摸。再到现在几乎被什么芒果,CIBN垄断,内容没什么可看,却什么都要收费。我始终抱有一个观点,如果电视盒子这一块开放发展,国内的厂家完全能够占领全世界的盒子市场,好几年前用的 Android 盒子就已经能够满足我的大部分需求,并且应用设计也早 Google 自己推出 Android TV 盒子以及规范 Android TV 应用好多年。可惜这一块市场被一道禁令打到了地下。
  • okhttp 使用 OkHttp是一个非常高效的HTTP客户端,默认情况下:
  • 斐讯 T1 盒子去除广告 斐讯投资送硬件这路子是着魔了,所以在斐讯 K2P 之后又入手了这个 T1 的盒子,配置2G RAM,16G Sdcard,配置还不错。接口有 USB,网口,HDMI 口,还有 一个 AV 输出口,日常使用是没有任何问题的,初尝试一下非常流畅,不过让我不爽的是,第一次进入竟然需要验证手机,说是 CIBN 盒子的验证,如果不注册还不让进入。