导出mongodb数据为CSV,并切割CSV文件为小文件

同事需要数据库里相关信息,用作分析。安要求需要将其导出为csv格式的,数据库使用的是mongodb,导出CSV格式并不难。数据库里所涉及到表有100多个,进去mongodb数据库:
> use yourdb;
>show collections;

将相关的表复制下来,保存到一个文档里面,以供后面使用,我这里保存为db.txt。

然后编写以下脚本:

1
2
3
4
5
6
7
8
9
10
11
12
 
#!/bin/bash

USERNAME="username"
PASSWORD="password"
BINPATH="/usr/local/mongodb/bin/"
 
for i in $(cat db.txt)
do
        echo "=====export $i============" >> /tmp/export.log
        ${BINPATH}mongoexport -u $USERNAME -p $PASSWORD -d db-c $i -f "birth_day,birth_month,birth_year,province,city,create_at,fansnum,idolnum,tweetnum,introduction,isvip,location,name,nick,openid,sex,tags,verifyinfo" -o /tmp/$i.csv --csv >> /tmp/export.log
done

-u 用户名
-p 密码
-d 数据库表
-c 表名,mongodb里叫集合名
-f 要导出的字段 如:”username,password”
-o 导出后保存的文件
–csv 标示导出csv格式

导出的时间长短视服务器性能和要导出数据多少而定,我大概使用了6个多小时才将数据完全导出。

导出发现有些CSV文件里面存放的数据过多,使用excel打开提示“数据过大,被截断”之类的信息。打开一个空的excel文档,发现里面最多只能保存65535行数据。所以,要想正确地打开导出的数据,必须将CSV文件切割为每个文件不多余65535行数据才行。
简单写了个脚本切割CSV文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
####
# split csv files
#
####
for i in $(ls -l /tmp/*.csv | awk '{print $NF}')
do
        FILENAME=$(echo $i | awk -F'/' '{print $NF}')
        echo "SPLIT:$i.......\n"
          ####每60000行保存为一个文件########
        split -l 60000 $i $FILENAME
done
echo "==========重命名文件,为其添加.csv后缀============="
for f in $(ls -l *.csv* | awk -F[" ""/"] '{print $NF}')
do
        echo "rename:$f...."
        mv $f $f.csv
     ####切割后,有些文件会没有表头信息,需要为其增加表头信息#########
       sed -i '1i\birth_day,birth_month,birth_year,province,city,create_at,fansnum,idolnum,tweetnum,introduction,isvip,location,name,nick,openid,sex,tags,verifyinfo' $f.csv
done

amoeba实现mysql读写分离,亲测

OS:centos6.1(64 bit)
amoeba主机:192.168.1.100
mysql master:192.168.1.101
mysql slave:192.168.1.102

amoeba要安装JDK环境,centos下:yum search java 查询相关组件,然后yum install **安装即可 :yum install java

vi /etc/profile

#添加如下两行内容

export JAVA_HOME=/usr/lib/jvm/jre-openjdk

export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH

#执行下面的命令使更改的内容生效

source /etc/profile

安装amoeba-mysql,amoeba-mysql,下载解压后即可使用

wget http://blogimg.chinaunix.net/blog/upfile2/101027160252.zip

mkdir /usr/local/amoeba/
****软件作者打包的习惯比较可爱,我们得先建个目录用来存放解压后的程序文件
cp 101027160252.zip /usr/local/amoeba/
cd /usr/local/amoeba/
unzip 101027160252.zip
cd bin/
chmod 755 amoeba

(bin 解压后bin下为程序的执行文件,conf 下为配置文件)
◆ amoeba.xml:主配置文件,配置所有数据源以及Amoeba 自身的参数设置;实现主从的话配置这个文件就可以了;

◆ rule.xml:配置所有Query 路由规则的信息;

◆ functionMap.xml:配置用于解析Query 中的函数所对应的Java 实现类;

◆ rullFunctionMap.xml:配置路由规则中需要使用到的特定函数的实现类;
下面我们就来通过更改amoeba.xml配置文件实现mysql主从读写分离,配置如下:

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<?xml version="1.0" encoding="gbk"?>
 
<!DOCTYPE amoeba:configuration SYSTEM "amoeba.dtd">
<amoeba:configuration xmlns:amoeba="http://amoeba.meidusa.com/">
 
    <server>
        <!-- proxy server绑定的端口 -->
        <property name="port">8066</property>
 
        <!-- proxy server绑定的IP -->
        <property name="ipAddress">192.168.1.100</property>
        <!-- proxy server net IO Read thread size -->
        <property name="readThreadPoolSize">20</property>
 
        <!-- proxy server client process thread size -->
        <property name="clientSideThreadPoolSize">30</property>
 
        <!-- mysql server data packet process thread size -->
        <property name="serverSideThreadPoolSize">30</property>
 
        <!-- socket Send and receive BufferSize(unit:K)  -->
        <property name="netBufferSize">128</property>
 
        <!-- Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm). -->
        <property name="tcpNoDelay">true</property>
 
        <!-- 对外验证的用户名 -->
        <property name="user">root</property>
 
        <!-- 对外验证的密码 -->
        <property name="password">password</property>
 
        <!-- query timeout( default: 60 second , TimeUnit:second) -->
        <property name="queryTimeout">60</property>
    </server>
 
    <!-- 
        每个ConnectionManager都将作为一个线程启动。
        manager负责Connection IO读写/死亡检测
    -->
    <connectionManagerList>
        <connectionManager name="defaultManager" class="com.meidusa.amoeba.net.MultiConnectionManagerWrapper">
            <property name="subManagerClassName">com.meidusa.amoeba.net.AuthingableConnectionManager</property>
 
            <!-- 
              default value is avaliable Processors 
            <property name="processors">5</property>
             -->
        </connectionManager>
    </connectionManagerList>
 
    <dbServerList>
        <!-- 
            一台mysqlServer 需要配置一个pool,
            如果多台 平等的mysql需要进行loadBalance, 
            平台已经提供一个具有负载均衡能力的objectPool:com.meidusa.amoeba.mysql.server.MultipleServerPool
            简单的配置是属性加上 virtual="true",该Pool 不允许配置factoryConfig
            或者自己写一个ObjectPool。
        -->
        <dbServer name="server1">
 
            <!-- PoolableObjectFactory实现类 -->
            <factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
                <property name="manager">defaultManager</property>
 
                <!-- 真实mysql数据库端口 -->
                <property name="port">3306</property>
 
                <!-- 真实mysql数据库IP -->
                <property name="ipAddress">192.168.1.101</property>
                <property name="schema">test</property>
 
                <!-- 用于登陆mysql的用户名 -->
                <property name="user">write</property>
 
                <!-- 用于登陆mysql的密码 -->
 
                <property name="password">write123456</property>
 
            </factoryConfig>
 
            <!-- ObjectPool实现类 -->
            <poolConfig class="com.meidusa.amoeba.net.poolable.PoolableObjectPool">
                <property name="maxActive">200</property>
                <property name="maxIdle">200</property>
                <property name="minIdle">10</property>
                <property name="minEvictableIdleTimeMillis">600000</property>
                <property name="timeBetweenEvictionRunsMillis">600000</property>
                <property name="testOnBorrow">true</property>
                <property name="testWhileIdle">true</property>
            </poolConfig>
        </dbServer>
 
 
        <dbServer name="server2">
 
            <!-- PoolableObjectFactory实现类 -->
            <factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
                <property name="manager">defaultManager</property>
 
                <!-- 真实mysql数据库端口 -->
                <property name="port">3306</property>
 
                <!-- 真实mysql数据库IP -->
                <property name="ipAddress">192.168.1.102</property>
                <property name="schema">test</property>
 
                <!-- 用于登陆mysql的用户名 -->
                <property name="user">read</property>
 
                <!-- 用于登陆mysql的密码 -->
 
                <property name="password">read123456</property>
 
            </factoryConfig>
 
            <!-- ObjectPool实现类 -->
            <poolConfig class="com.meidusa.amoeba.net.poolable.PoolableObjectPool">
                <property name="maxActive">200</property>
                <property name="maxIdle">200</property>
                <property name="minIdle">10</property>
                <property name="minEvictableIdleTimeMillis">600000</property>
                <property name="timeBetweenEvictionRunsMillis">600000</property>
                <property name="testOnBorrow">true</property>
                <property name="testWhileIdle">true</property>
            </poolConfig>
        </dbServer>
 
 
        <dbServer name="master" virtual="true">
            <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
                <!-- 负载均衡参数 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->
                <property name="loadbalance">1</property>
 
                <!-- 参与该pool负载均衡的poolName列表以逗号分割 -->
                <property name="poolNames">server1</property>
            </poolConfig>
        </dbServer>
 
        <dbServer name="slave" virtual="true">
            <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
                <!-- 负载均衡参数 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->
                <property name="loadbalance">1</property>
 
                <!-- 参与该pool负载均衡的poolName列表以逗号分割 -->
                <property name="poolNames">server1,server2</property>
            </poolConfig>
        </dbServer>
 
    </dbServerList>
 
    <queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter">
        <property name="ruleConfig">${amoeba.home}/conf/rule.xml</property>
        <property name="functionConfig">${amoeba.home}/conf/functionMap.xml</property>
        <property name="ruleFunctionConfig">${amoeba.home}/conf/ruleFunctionMap.xml</property>
        <property name="LRUMapSize">1500</property>
        <property name="defaultPool">master</property>
 
        <property name="writePool">master</property>
        <property name="readPool">slave</property>
        <property name="needParse">true</property>
    </queryRouter>
</amoeba:configuration>

在两台数据库服务器分别执行:
mast:grant all privileges on test.* to ‘write’@’192.1681.100′ identified by ‘write123456′;
slave:grant all privileges on test.* to ‘read’@’192.168.1.100′ identified by ‘read123456′;

amoeba主机上启动amoeba:
/usr/local/amoeba/bin/amoeba &

lsof -i:8066
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 22233 root 68u IPv6 25399006 0t0 TCP 192.168.1.100:8066 (LISTEN)

成功启动,端口已经在监听

找他其他服务器连接amoeba主机
mysql -h 192.168.1.100 -P 8066 -u root -ppassword

测试读写分离,测试的时候先在slave上执行mysql>stop slave;暂停主从,否则读写分离看不出效果。

简单主从权重配置

大家可能会想到,我们加入只有两台数据库服务器,一台主,一台从,按照上面的配置只能是主和从的读取比率是1:1,而写又全部在主上进行,这样主的压力就很大了,所以如果能让主和从的读设置权重,比如设置成1:3,这样就可以很好的解决主从不压力均衡的问题!通过研究确实可以!

配置就是将上面的读的池的配置更改一下:

server1,server2更改成

server1,server2,server2,server2

我测试的结果刚好为1:3:

mysql> select * from sync;
+——+——-+
| id | title |
+——+——-+
| 1 | wwwww |
| 100 | slave |
+——+——-+
2 rows in set (0.00 sec)

mysql> select * from sync;
+——+——-+
| id | title |
+——+——-+
| 1 | wwwww |
| 100 | slave |
+——+——-+
2 rows in set (0.01 sec)

mysql> select * from sync;
+——+——-+
| id | title |
+——+——-+
| 1 | wwwww |
| 100 | slave |
+——+——-+
2 rows in set (0.00 sec)

mysql> select * from sync;
+——+——-+
| id | title |
+——+——-+
| 1 | wwwww |
| 2 | ddddd |
+——+——-+
2 rows in set (0.00 sec)

PHP获得前一天、后一天时间的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
date_default_timezone_set('PRC'); //默认时区
 
echo "今天:",date("Y-m-d",time()),"<br>";
echo "今天:",date("Y-m-d",strtotime("18 june 2008")),"<br>";
echo "昨天:",date("Y-m-d",strtotime("-1 day")), "<br>";
echo "明天:",date("Y-m-d",strtotime("+1 day")), "<br>";
echo "一周后:",date("Y-m-d",strtotime("+1 week")), "<br>";
echo "一周零两天四小时两秒后:",date("Y-m-d G:H:s",strtotime("+1 week 2 days 4 hours 2 seconds")), "<br>";
echo "下个星期四:",date("Y-m-d",strtotime("next Thursday")), "<br>";
echo "上个周一:".date("Y-m-d",strtotime("last Monday"))."<br>";
echo "一个月前:".date("Y-m-d",strtotime("last month"))."<br>";
echo "一个月后:".date("Y-m-d",strtotime("+1 month"))."<br>";
echo "十年后:".date("Y-m-d",strtotime("+10 year"))."<br>"

jQuery队列使用相关

最近工作中需要用到jquery的队列来顺序地向服务器发送请求,翻了个手册,网上查了下资料,总算能顺利使用了,现总结一下
jQuery核心中, 有一组队列控制方法, 这组方法由queue()/dequeue()/clearQueue()三个方法组成, 它对需要连续按序执行的函数的控制可以说是简明自如, 主要应用于animate ()方法, ajax以及其他要按时间顺序执行的事件中.

queue(name,[callback]): 当只传入一个参数时, 它返回并指向第一个匹配元素的队列(将是一个函数数组,队列名默认是fx); 当有两个参数传入时, 第一个参数还是默认为fx的的队列名, 第二个参数又分两种情况, 当第二个参数是一个函数时, 它将在匹配的元素的队列最后添加一个函数. 当第二个参数是一个函数数组时,它将匹配元素的队列用新的一个队列来代替(函数数组).(name为该队列的队列名,callback队列里面的执行函数,是个数组)
dequeue(name): 这个好理解, 就是从队列最前端移除一个队列函数, 并执行它.
clearQueue([queueName]):这是1.4新增的方法. 清空对象上尚未执行的所有队列. 参数可选,默认为fx. 但个人觉得这个方法没多大用, 用queue()方法传入两个参数的第二种参数即可实现clearQueue方法.

//声明一个空数组来存放队列的执行函数
Var postArr = [];
//将执行的函数存入数组
postArr.push(function(){
//具体要执行的操作代码
$.post(‘test.php’,{‘id’:1000},function(data){//返回函数操作},’json’);
//执行初始函数,取出队列中的第一个事件并执行
_takePst();
});
//最后再PUSH一个队列执行完毕后的函数进去,显示执行的结果
postArr.push(function(){alert(‘队列执行完毕’);});
//声明一个队列
$(document).queque(‘postList’,postArr);
Var _takePst = function(){
$(document).dequeque(‘postList’);
}

//初始化队列执行,将队列中的第一个事件取出并执行
_takePst();
这样的话 浏览器会一个一个地发送请求,第一个请求完毕后发送第二个。但是这样的话 每个事件的发送是连续的一个完毕后马上发送第二个,中间没有一定的时间间隔,对两个操作之间要间隔一定时间的应用基本上只有第一个请求能执行成功。此时需要在队列发送时间请求的时间间隔一定的时间,如下边代码
postArr.push(function(){setTimeout(function(){
//具体的时间操作代码
_takePst();
},5000);});
参考链接

http://blog.chapagain.com.np/jquery-set-time-interval-between-events-with-queue-function/

http://www.ajaxcn.net/archives/1526

MongoDB的备份与恢复

最近在学习MongoDB,一直在看《MongoDB权威指南》,O’REILLY的书值得阅读!~~~
MongoDB的备份就像Mysql一样,可以停掉服务,直接备份数据存放目录。但这种方法基本上不可能在线上环境使用。MongoDB自带一个工具mongodump,可以对运行的MongoDB做查询,然后将所有查到的文档写入磁盘。mongodump使用的是普通的查询,所以产生的备份不一定是数据库的实时快照,服务器在备份过程中处理写入时尤为明显。
[root@localhost bin]# ./mongodump -u duizhang -p123456 -d test -o ~/backup
-u 用户名 -p密码 -d 所要备份的数据库,如实例所示,duizhang这个账号需要对test有只读权限即可。如果启动mongodb时没有加–auth参数,可不用用户名密码选项。-o 备份数据存放目录。

使用mongorestore获取mongodump的输出结果,并将备份的数据插入到运行的mongodb实例中。
[root@localhost bin]# ./mongorestore -u duizhang -p123456 -d test –drop ~/backup/test/
-d 指定了要恢复的数据库,这里为test,这个选项可以将备份恢复到不同名的数据库中。–drop代表在恢复前删除集合,如果存在的话。否则,数据会与现有的集合数据合并,会覆盖一些文档。恢复的时候要使用管理员账号密码。如果启动时没有–auth参数,可忽略-u -p选项。

MongoDB自动备份shell脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
 
MONGODBDIR="/usr/local/mongodb/bin"
TODAY=$(date +%Y-%m-%d)
BACKUP="/backup/"
USERNAME="duizhang"
PASSWORD="123456"
DB="test"
 
[ ! -d $BACKUP ] && mkdir $BACKUP
[ ! -d ${BACKUP}${TODAY} ] && mkdir -p ${BACKUP}${TODAY}
for db in $DB
do
        ${MONGODBDIR}/mongodump -u $USERNAME -p${PASSWORD} -d $db -o ${BACKUP}${TODAY}
done

然后加入crontab。

mysql备份shell脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
 
 
MYSQLCMD="/usr/bin/"
BACKDIR="/backup"
TODAY=$(date +%Y-%m-%d)
BDIR=${BACKDIR}/${TODAY}
DBHOST="localhost"
DBUSER="uname"
DBPASS="password"
MYSQLDB=$(${MYSQLCMD}/mysql -h $DBHOST -u $DBUSER -p$DBPASS -e "SHOW DATABASES;")
 
[ ! -d $BACKDIR ] && mkdir $BACKDIR
[ ! -d $BDIR ] && mkdir -p $BDIR
 
 
for dbname in $MYSQLDB
do
        [[ $dbname != "Database" && $dbname != "information_schema" ]] && ${MYSQLCMD}/mysqldump -h $DBHOST -u $DBUSER -p$DBPASS --default-character-set=utf8 --opt $dbname > ${BDIR}/${dbname}.sql
#               [ $? -eq 0 ] && echo -e "$dbname backup successfull"
done
 
#cd $BACKDIR
#tar cvzf ${TODAY}-mysqlbackup.tar.gz $TODAY

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
	define('WEB_ROOT', dirname(__FILE__));
	/*
	* image upload class
	* $maxsize int 允许上传的最大文件 单位:K
	* $file 上传的文件域 $_FILES['pic']
	* $errorlog true/false 是否记录上传错误日志
	* $Aextend 允许上传的后缀名
	* $path 上传文件保存的路径
	* $tpath 缩略图保存的路径
	* $twidth 缩略图的宽度
	* $theight 缩略图的高度
	* $thumb 是否生成缩略图
	* $newname 上传后文件的保存名
	* $thumbname 缩略文件名 完整路径
	* $picname 图片保存的完整路径
	* 例子:$up = new Upload($_FILES['pic']);
	*		$up->DoUpload();
	*/
	class Upload{
		private $maxsize;
		private $file;
		private $errorlog ;
		private $Aextend = array('jpg','gif','png');
		private $path;
		private $tpath;
		private $twidth;
		private $theight;
		private $thumb;
		private $newname;
		private $thumbname = '';
		private $picname;
		function __construct($file,$thumb = true,$twidth = 100,$theight = 100,$tpath = './thumb000000/',$path = './upload000000/',$maxsize = 10240000,$errorlog = true){
			$this->maxsize = $maxsize;
			$this->file = $file;
			$this->errorlog = $errorlog;
			$this->path = $path;
			$this->tpath = $tpath;
			$this->thumb = $thumb;
			$this->twidth = $twidth;
			$this->theight = $theight;
		}
 
		private function GetExtend(){
			$type = $this->file['type'];
			$extend = '';
			switch($type){
				case 'image/pjpeg' :
				case 'image/jpeg' :
					$extend = "jpg";
					break;
				case 'image/gif' :
					$extend = "gif";
					break;
				case 'image/png' :
					$extend = "png";
					break;
 
			}
			return $extend;
		}
		private function GetSize(){
			return $this->file['size']/1024;
		}
		private function Mkdirs($p){
			return mkdir($p,0777)&&file_put_contents($p.'/index.htm',' ');
		}
		private function GetNewName(){
			return $this->newname = 'duizhang_'.time().'.'.$this->GetExtend();
		}
		//private function GetThumbName(){
		//	return $this->thumbname = $this->tpath.'duizhang_'.time().'.'.$this->GetExtend();
		//}
		/*
		* 生成缩略图和原始图目录
		* 如原始图目录:./upload000000/2011/06/11/
		*/
		private function DoMkdirs(){
			$year = date('Y',time()+8*60*60);
			$month = date('m',time()+8*60*60);
			$day = date('d',time()+8*60*60);
			try{
				if(!is_dir($this->path)){
						if(!$this->Mkdirs($this->path)) throw new FileException('上传文件',2);
					}
 
				if(!is_dir($this->path.$year)){
					if(!$this->Mkdirs($this->path.$year)) throw new FileException('上传文件',2);
				}
				if(!is_dir($this->path.$year.'/'.$month)){
					if(!$this->Mkdirs($this->path.$year.'/'.$month)) throw new FileException('上传文件',2);
				}
				if(!is_dir($this->path.$year.'/'.$month.'/'.$day)){
					if(!$this->Mkdirs($this->path.$year.'/'.$month.'/'.$day)) throw new FileException('上传文件',2);
				}
				$this->path = $this->path.$year.'/'.$month.'/'.$day.'/';
 
 
				if($this->thumb === true){
					if(!is_dir($this->tpath)){
						if(!$this->Mkdirs($this->tpath)) throw new FileException('上传文件',2);
					}
					if(!is_dir($this->tpath.$year)){
						if(!$this->Mkdirs($this->tpath.$year)) throw new FileException('上传文件',2);
					}
					if(!is_dir($this->tpath.$year.'/'.$month)){
						if(!$this->Mkdirs($this->tpath.$year.'/'.$month)) throw new FileException('上传文件',2);
					}
					if(!is_dir($this->tpath.$year.'/'.$month.'/'.$day)){
						if(!$this->Mkdirs($this->tpath.$year.'/'.$month.'/'.$day)) throw new FileException('上传文件',2);
					}
					$this->tpath = $this->tpath.$year.'/'.$month.'/'.$day.'/';
				}
 
			}catch(FileException $e){
				$errormessage = $e->Getmessage().'---'.$e->GetDetail();
				echo $errormessage;
				if($this->errorlog){
					DoLog::Instance('errorupload');
					DoLog::WriteLog($errormessage);
				}
			}
 
		}
 
 
		//执行上传
		function DoUpload(){
			$this->DoMkdirs();
			try{
				if(!in_array($this->GetExtend(),$this->Aextend)) throw new FileException('上传文件',0);
				if($this->GetSize() > $this->maxsize || $this->GetSize()<=0) throw new FileException('上传文件',1);
				if(!is_writable($this->path)) throw new FileException('上传文件',7);
				if(!is_writable($this->tpath)) throw new FileException('上传文件',7);
				if(!move_uploaded_file($this->file['tmp_name'],$this->path.$this->GetNewName())) throw new FileException('上传文件',8);
				$this->picname = $this->path.$this->newname;
 
				if($this->thumb === true){
					if(!$this->ThumbImg($this->twidth,$this->theight,$this->path.$this->newname,$this->tpath.$this->newname,$this->GetExtend())) throw new FileException('上传文件',9);
					$this->thumbname = $this->tpath.$this->newname;
				}
 
			}catch(FileException $e){
				$errormessage = $e->Getmessage().'---'.$e->GetDetail();
				echo $errormessage;
				if($this->errorlog){
					DoLog::Instance('errorupload');
					DoLog::WriteLog($errormessage);
				}
			}
		}
 
		/**
		 * 
		 * 图片缩略方法
		 * @param int $twidth 图片宽度
		 * @param int 图片长度
		 * @param string $patch 原图片保存路径
		 * @param string $tpath 缩略图保存路径
		 * @param string $type 要缩略的图片的类型(后缀)
		 */
		private function ThumbImg($twidth,$theight,$patch,$tpath,$type){
			switch ($type)
			{
				case "jpg":
					$image2=imagecreatefromjpeg($patch);
					function outputimg($image,$path){imagejpeg($image,$path);};
					break;
				case "gif":
					$image2=imagecreatefromgif($patch);
					function outputimg($image,$path){imagegif($image,$path);};
					break;
				case "png":
					$image2=imagecreatefrompng($patch);
					function outputimg($image,$path){imagepng($image,$path);};
					break;
			}
 
			$image=imagecreatetruecolor($twidth, $theight);
			if($type=="png"){
				imagesavealpha($image2,true);//设置png透明alpha通道开启
				imagealphablending($image,false);//不合并alpha通道图层
				imagesavealpha($image,true);
			}
			$image2x=imagesx($image2);
			$image2y=imagesy($image2);
			imagecopyresampled($image, $image2, 0, 0, 0, 0, $twidth, $theight, $image2x, $image2y);
			outputimg($image,$tpath);
			imagedestroy($image2);
			imagedestroy($image);
			return true;
		}
 
 
		function PicName(){
			return $this->picname;
		}
 
		function ThumbName(){
			return $this->thumbname;
		}
	}
	//自定义文件异常处理类
	class FileException extends Exception{
		 function GetDetail(){
			switch($this->code){
				case 0:
					return $this->GetTime()."\t".'上传文件类型错误';
					break;
				case 1:
					return $this->GetTime()."\t".'上传文件大小超过限制';
					break;
				case 2:
					return $this->GetTime()."\t".'创建上传目录失败';
					break;
				case 3:
					return $this->GetTime()."\t".'创建日志记录目录失败';
					break;	
				case 4:
					return $this->GetTime()."\t".'创建日志文件失败';
					break;
				case 5:
					return $this->GetTime()."\t".'打开日志文件失败';
					break;
				case 6:
					return $this->GetTime()."\t".'写入日志文件失败';
					break;
				case 7:
					return $this->GetTime()."\t".'上传目录无写入权限';
					break;
				case 8:
					return $this->GetTime()."\t".'上传文件失败';
					break;
				case 9:
					return $this->GetTime()."\t".'缩略图生成失败';
					break;
				default:
					return $this->GetTime()."\t".'当前操作出现异常';
					break;		
			}
		}
 
		function GetTime(){
			return date('Y-m-d H:i:s',time()+8*60*60);
		}
 
	}
 
	///记录错误日志
	//如:上传文件错误日志存放在根目录下/log/errorupload/20110610.txt
	class DoLog{
		static private $dir = './log';
		static private $path;
		static private $message = '日志记录';
		private function __construct(){}
 
		/*
		*日志类 初始化方法
		*$type 记录日志的类型 如:上传错误日志 类型为 errorupload
		*/
		static public function Instance($type){
			if(!is_dir(self::$dir)) self::Mkdirs(self::$dir);
			if(!is_dir(self::$dir.'/'.$type)) self::Mkdirs(self::$dir.'/'.$type);
			self::$path = self::$dir.'/'.$type.'/'.date('Y-m-d',time()+8*60*60).'.txt';
			if(!file_exists(self::$path)) self::Touch();
 
		}
		/*创建日志目录*/
		static private function Mkdirs($d){
			try{
				if(!mkdir($d,0777)) throw new FileException(self::$message.'---'.$d,3);
			}catch(FileException $e){
				echo $e->Getmessage().'===='.$e->GetDetail();
			}
		}
		/*创建日志文件*/
		static private function Touch(){
			try{
				if(!touch(self::$path)) throw new FileException(self::$path,4);
			}catch(FileException $e){
				echo $e->Getmessage().'===='.$e->GetDetail();
			}
		}
		/*
		*讲错误日志写入文件
		*$m 错误日志信息
		*/
		static public function WriteLog($m){
			try{
				if(!$fp = fopen(self::$path,'a+')) throw new FileException(self::$path,5);
				if(!fwrite($fp,$m."\r\n")) throw new FileException(self::$path,6);
				fclose($fp);
			}catch(FileException $e){
				echo $e->Getmessage().'===='.$e->GetDetail();
			}
		}
	}

PHP的FTP上传类

记不清什么时候写的了。^_^

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
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
* FTP class
* Author duizhang
* EXAMPLE:
* $ftp = new FTP('127.0.0.1','ftpuser','ftppass','/');
* $ftp->FtpMkdir('images'); mkdir images
* $ftp->FtpChdir('images'); change the current dir
* $ftp->FtpPut('remote.file.jpg','local.file.jpg',FTP_BINARY); Upload the file to FTP server
* */
class FTP{
	private $ftp_cid;
	private $func;
	private $ftphost;
	private $ftpuser;
	private $ftppass;
	private $port;
	private $timeout;
	private $pasv;
	private $ftppath;
	function __construct($ftphost,$ftpuser,$ftppass,$ftppath,$port = 21,$pasv = 0,$ftpssl = 0,$timout = 30){		
		@set_time_limit(0);
		$this->ftphost = $ftphost;
		$this->ftpuser = $ftpuser;
		$this->ftppass = $ftppass;
		$this->port = intval($port);
		$this->timeout = intval($timout);
		$this->pasv = $pasv;
		$this->ftppath = $ftppath;
		$this->func = $ftpssl && function_exists('ftp_ssl_connect') ? 'ftp_ssl_connect' : 'ftp_connect';
		try{
			if($this->func == 'ftp_connect' && !function_exists('ftp_connect')) throw new Exception('FTP not supported.');
		}catch (Exception $e){
			echo $e->getMessage();
		}
		$this->FtpLogin();
	}
	/*
	* ftplogin method
	* */
	private function FtpLogin(){
		$func = sprintf('%s',$this->func);
		try{
			if(!$this->ftp_cid = @$func($this->ftphost,$this->port,20)) throw new Exception('FTP connected failed.');
		}catch (Exception $e){
			echo $e->getMessage();
		}
 
		if($this->timeout && function_exists('ftp_set_option')) {
			@ftp_set_option($ftp_conn_id, FTP_TIMEOUT_SEC, $this->timeout);
		}
		try{
			if(!ftp_login($this->ftp_cid,$this->ftpuser,$this->ftppass)) throw new Exception('530 NOT LOGGED IN.');
 
		}catch(Exception $e){
			echo $e->getMessage();
		}
		if($this->pasv) @ftp_pasv($this->ftp_cid,$this->pasv);
		try{
			if(!$this->FtpChdir($this->ftppath)) throw new Exception('chdir'.$this->ftppath.' error.');
		}catch(Exception $e){
			echo $e->getMessage();
		}
		//return $this->cid;
	}
 
	/*
	* ftp mkdir
	* */
	function FtpMkdir($path){
		$path = $this->wipespecial($path);
		return @ftp_mkdir($this->ftp_cid,$path);
	}
	/*
	* ftp rmdir
	* */
	function FtpRmdir($path){
		$path = $this->wipespecial($path);
		return @ftp_rmdir($this->ftp_cid,$path);
	}
	/*
	* FTP put file
	* $remote_file remote file path
	* $local_file local file path
	* $mode FTP_ASCII/FTP_BINARY
	* */
	function FtpPut($remote_path,$local_path,$mode,$startpos = 0){
		$remote_path = $this->wipespecial($remote_path);
		$local_path = $this->wipespecial($local_path);
		try{
			if(!in_array($mode,array(FTP_ASCII,FTP_BINARY))) throw new Exception('FTP MODE IS ERROR');
		}catch (Exception $e){
			echo $e->getMessage();
		}
		//echo $remote_path;
		return ftp_put($this->ftp_cid,$remote_path,$local_path,$mode,$startpos);
	}
	/*
	* FTP DELETE THE REMOTE FILE
	* */
	function FtpDelete($path){
		$path = $this->wipespecial($path);
		return @ftp_delete($this->ftp_cid,$path);
	}
 
	/*
	* get the remote file size
	* */
	function FtpSize($path){
		$path = $this->wipespecial($path);
		return ftp_size($this->ftp_cid,$path);
	}
 
	/*
	* FTP CHDIR
	* */
	function FtpChdir($path){
		$path = $this->wipespecial($path);
		return ftp_chdir($this->ftp_cid,$path);
	}
 
	/*
	*FTP CHMOD
	* */
	function FtpChmod($path){
		$path = $this->wipespecial($path);
		return ftp_chmod($this->ftp_cid,0777,$path);
	}
	/*
	*
	* */
	private function wipespecial($str) {
		return str_replace(array( "\n", "\r", '..'), array('', '', ''), $str);
	}
	function __destruct(){
		ftp_close($this->ftp_cid);
	}
}