一、背景
通过python脚本上传图片到阿里云的OSS存储桶内。
二、环境
群晖存储设备,该设备上没有crontab命令,配置定时任务必须指明用户。
操作系统:Linux RackStation 3.10.102 #15101 SMP Fri Apr 28 02:48:03 CST 2017 x86_64 GNU/Linux synology_broadwell_rs18017xs+
三、报错信息
Traceback (most recent call last):
File "/volume1/scripts/backupToOSS.py", line 59, in
bucket.put_object_from_file(ossName, tfile)
File "/usr/lib/python2.7/site-packages/oss2/api.py", line 380, in put_object_from_file
with open(to_unicode(filename), 'rb') as f:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 9-10: ordinal not in range(128)
Traceback (most recent call last):
File "/volume1/scripts/backupToOSS.py", line 59, in
bucket.put_object_from_file(ossName, tfile)
File "/usr/lib/python2.7/site-packages/oss2/api.py", line 380, in put_object_from_file
with open(to_unicode(filename), 'rb') as f:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 9-10: ordinal not in range(128)
从报错信息看,应该是脚本中在执行bucket.put_object_from_file(ossName, tfile)的时候,调用/usr/lib/python2.7/site-packages/oss2/api.py报错的,
四、整个python脚本
#!/usr/bin/env python
#-*- coding: utf-8 -*-
#Filename: backupToOSS.py
#Author:
#Desc:该脚本用于把给定的文件传输到阿里云的OSS中。
import os
import datetime
import commands
import oss2
import logging
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# 首先初始化AccessKeyId、AccessKeySecret、Endpoint等信息。
# 通过环境变量获取,或者把诸如“<你的AccessKeyId>”替换成真实的AccessKeyId等。
#
# 以杭州区域为例,Endpoint可以是:
# http://oss-cn-hangzhou.aliyuncs.com
# https://oss-cn-hangzhou.aliyuncs.com
# 分别以HTTP、HTTPS协议访问。
access_key_id = os.getenv('OSS_TEST_ACCESS_KEY_ID', 'aaaaaaaaaaaaaaaaaa')
access_key_secret = os.getenv('OSS_TEST_ACCESS_KEY_SECRET', 'xxxxxxxxxxxx')
bucket_name = os.getenv('OSS_TEST_BUCKET', 'yyyyyyyy')
endpoint = os.getenv('OSS_TEST_ENDPOINT', 'zzzzzzzzzzzzzzzz')
# 确认上面的参数都填写正确了
for param in (access_key_id, access_key_secret, bucket_name, endpoint):
assert '<' not in param, '请设置参数:' + param
# 创建Bucket对象,所有Object相关的接口都可以通过Bucket对象来进行
bucket = oss2.Bucket(oss2.Auth(access_key_id, access_key_secret), endpoint, bucket_name)
cmdYsd = 'date -d "yesterday" +"%Y%m%d"'
#getoutput(cmd)方法返回的是一个字符串,用于构成变动文件名称列表文件名
oYsd = commands.getoutput(cmdYsd)
#findWithOSS.sh脚本生成的前一天变动文件的名称列表文件
dataFile = "/opt/tmp/datadir/filelist.log-" + oYsd
logging.basicConfig(filename="/opt/tmp/osslogs/ossError.log-" + oYsd, level=logging.ERROR)
#把本地文件上传到OSS,新的Object名称存储在变量"ossName”中,ossName的命名规则:1,使用UTF-8编码;2,长度必须在1-1023字节之间;3,不能以“/”或者“\”字符开头。
fileListFile = open(dataFile, 'r')
fileList = fileListFile.readlines()
fileListFile.close()
with open("/opt/tmp/osslogs/ossTransferedFiles.log-"+oYsd, 'a+', buffering=1024) as transLog:
for tfile in fileList:
tfile = tfile.strip()
ossName = tfile.lstrip('/')
# fexist = bucket.object_exists(ossName)
# print fexist
#判断文件在oss上是否存在,如果不存在则上传,这样可以避免文件重传浪费时间;
# if not fexist:
nowTime = datetime.datetime.now().strftime('%Y%m%d_%T')
try:
bucket.put_object_from_file(ossName, tfile)
transLog.write("{0} {1}\n".format(nowTime, tfile))
except IOError:
logging.exception("######"+nowTime+"######")
except:
logging.exception("######"+nowTime+"######")
tmpFile = open("/opt/tmp/osslogs/ossCurrentFileWhenExit.log", 'w+')
tmpFile.write("{0} {1}\n".format(nowTime, tfile))
tmpFile.close()
sys.exit()
五、crontab配置
#backup data to Aliyun OSS
30 00 * * * root /bin/bash /volume1/scripts/findWithOSS.sh
30 09 * * * root /bin/python /volume1/scripts/backupToOSS.py
六、诡异现象
奇怪的是,完全一样的脚本,使用crontab跑定时任务就会报上面的错误,但是手动执行就完全没有问题:
nohup python ./backupToOSS.py &
七、原因分析
crontab执行脚本时的环境变量跟直接以root用户身份手动执行的时候环境变量不同。
八、解决办法
在/etc/crontab文件中添加一条载入命令source即可:经过测试这种方法生效了,没有再报错。
#backup data to Aliyun OSS
30 00 * * * root /bin/bash /volume1/scripts/findWithOSS.sh
55 10 * * * root source /etc/profile && /bin/python /volume1/scripts/backupToOSS.py