Symfoware

Symfowareについての考察blog

Python + pyftpdlibでカスタムFTPサーバーを作成4 データベース連携

pyftpdlibで独自のFTPサーバーを作ってみています。
Python + pyftpdlibでカスタムFTPサーバーを作成3 ファイルの取得

ユーザー認証や、仮想のファイルシステムの作り方がわかってきました。
これらをデータベースで管理するようにしてみます。



テーブルの作成



データベースはmariadbを使用することにしました。

userテーブルでユーザー情報の管理。
folderテーブルでフォルダー情報の管理。
imageでアップロードされた画像の管理を行います。

簡単なサンプルにするため、フォルダーは1階層のみです。
ログインするユーザーにより、表示されるフォルダーの内容が変わります。


create table user (
id serial primary key,
name char(10) not null,
password char(10) not null
) engine = InnoDB;

create table folder (
id serial primary key,
maketime datetime not null,
name varchar(100),
owner bigint
) engine = InnoDB;

create table image (
id serial primary key,
folder_id bigint,
maketime datetime not null,
size bigint,
data longblob
) engine = InnoDB;





登録機能は無いので、動作確認用にこんなスクリプトで
適当にデータを投入しておきました。


  1. # -*- coding:utf-8 -*-
  2. import MySQLdb
  3. import datetime
  4. def create_user(name, password, cur):
  5.     
  6.     query = u'insert into user (name, password) values (%s, %s)'
  7.     cur.execute(query, [name, password])
  8. def create_folder(maketime, name, owner, cur):
  9.     
  10.     query = u'insert into folder (maketime, name, owner) values (%s, %s, %s)'
  11.     cur.execute(query, [maketime, name, owner])
  12. def insert_image(folder_id, maketime, cur):
  13.     query = u'insert into image (folder_id, maketime, size, data) values (%s, %s, %s, %s)'
  14.     
  15.     with open('06.jpg', 'rb') as f:
  16.         data = f.read()
  17.         cur.execute(query, [folder_id, maketime, len(data), data])
  18. def main():
  19.     con = MySQLdb.connect(
  20.         host='192.168.1.110',
  21.         db='ftp',
  22.         user='admin',
  23.         passwd='P@ssw0rd',
  24.         charset='utf8')
  25.     cur = con.cursor()
  26.     
  27.     #create_user('symfo', 'pass', cur)
  28.     
  29.     #create_folder(datetime.datetime(2014, 4, 11), u"テスト", 1, cur)
  30.     
  31.     insert_image(1, datetime.datetime(2014, 4, 11), cur)
  32.     
  33.     con.commit()
  34.     cur.close()
  35.     con.close()
  36. if __name__ == '__main__':
  37.     main()







サーバープログラム



全くエラートラップしていないですが、サーバープログラムは
こんな感じになりました。


  1. # -*- coding:utf-8 -*-
  2. from pyftpdlib.authorizers import AuthenticationFailed
  3. from pyftpdlib.handlers import FTPHandler
  4. from pyftpdlib.servers import FTPServer
  5. from cStringIO import StringIO
  6. import MySQLdb
  7. class MyAuthorizer(object):
  8.     
  9.     # ユーザー名、パスワードで認証
  10.     def validate_authentication(self, username, password, handler):
  11.         
  12.         msg = "Authentication failed."
  13.         
  14.         params = {
  15.             'name' : username,
  16.             'password' : password
  17.         }
  18.         
  19.         rows = execute(u'select id from user where name = %(name)s and password = %(password)s', params)
  20.         
  21.         if len(rows) != 1:
  22.             raise AuthenticationFailed(msg)
  23.     
  24.     # ユーザーのホームディレクトリを返す
  25.     def get_home_dir(self, username):
  26.         
  27.         params = { 'name' : username }
  28.         rows = execute(u'select id from user where name = %(name)s', params)
  29.         
  30.         return unicode(rows[0]['id'])
  31.     
  32.     # アクセス権限チェック
  33.     def has_perm(self, username, perm, path=None):
  34.         return 'elr'
  35.     
  36.     def get_perms(self, username):
  37.         return 'elr'
  38.     
  39.     # ログイン時に表示するメッセージ
  40.     def get_msg_login(self, username):
  41.         return 'Login successful.'
  42.     
  43.     # ログアウト時に表示するメッセージ
  44.     def get_msg_quit(self, username):
  45.         return 'Goodbye.'
  46.         
  47.     # ファイルにアクセスする前のフック
  48.     def impersonate_user(self, username, password):
  49.         pass
  50.     
  51.     # ファイルにアクセスした後のフック
  52.     def terminate_impersonation(self, username):
  53.         pass
  54.         
  55. class MyFileSystem(object):
  56.     
  57.     def __init__(self, root, cmd_channel):
  58.         self._cwd = u'/'
  59.         self._root = root
  60.         self.cmd_channel = cmd_channel
  61.     
  62.     # 現在のカレントディレクトリ
  63.     @property
  64.     def cwd(self):
  65.         return self._cwd
  66.         
  67.     # ftp要求パスをリアルパスに変換
  68.     def ftp2fs(self, ftppath):
  69.         return ftppath
  70.     
  71.     # 要求パスの妥当性検証
  72.     def validpath(self, path):
  73.         return True
  74.     
  75.     
  76.     # 指定されたパスがディレクトリであるか
  77.     def isdir(self, path):
  78.         return True
  79.     
  80.     # ディレクトリの内容をリストアップ
  81.     def listdir(self, path):
  82.         
  83.         if path == '/':
  84.             query = u"select name from folder where owner = %(owner)s"
  85.             rows = execute(query, {'owner':self._root})
  86.             dir_list = []
  87.             for row in rows:
  88.                 dir_list.append(row['name'])
  89.             
  90.             return dir_list
  91.         
  92.         folder_id = self._get_folder_id(path)
  93.         query = u"select id from image where folder_id = %(folder_id)s"
  94.         rows = execute(query, {'folder_id':folder_id})
  95.         dir_list = []
  96.         for row in rows:
  97.             dir_list.append(u'%(id)s.jpg' % row)
  98.         
  99.         return dir_list
  100.         
  101.     # フォーマット済の内容を返す
  102.     def format_mlsx(self, basedir, listing, perms, facts, ignore_err=True):
  103.         
  104.         if basedir == '/':
  105.         
  106.             query = u"select id, date_format(maketime, '%%Y%%m%%d%%k%%i%%s') as modify, name from folder where owner = %(owner)s"
  107.             
  108.             rows = execute(query, {'owner':self._root})
  109.             for row in rows:
  110.                 line = u'type=dir;size=0;perm=el;modify=%(modify)s; %(id)s.%(name)s\r\n' % row
  111.                 yield line.encode('utf8', self.cmd_channel.unicode_errors)
  112.         
  113.         else:
  114.             
  115.             folder_id = self._get_folder_id(basedir)
  116.             query = u"select id, date_format(maketime, '%%Y%%m%%d%%k%%i%%s') as modify, size from image where folder_id = %(folder_id)s"
  117.             rows = execute(query, {'folder_id':folder_id})
  118.             for row in rows:
  119.                 line = u'type=file;size=%(size)s;perm=r;modify=%(modify)s; %(id)s.jpg\r\n' % row
  120.                 yield line.encode('utf8', self.cmd_channel.unicode_errors)
  121.         
  122.         #line = u'type=dir;size=0;perm=el;modify=20071127230206; 1\r\n'
  123.         #yield line.encode('utf8', self.cmd_channel.unicode_errors)
  124.         
  125.         #line = u'type=file;size=202103;perm=r;modify=20071029155301; 2.png\r\n'
  126.         #yield line.encode('utf8', self.cmd_channel.unicode_errors)
  127.     
  128.     
  129.     def _get_folder_id(self, path):
  130.         folder_name = path.split('/')[-1]
  131.         folder_id = folder_name.split('.')[0]
  132.         return folder_id
  133.     
  134.     
  135.     def chdir(self, path):
  136.         
  137.         if path.startswith(u'/'):
  138.             self._cwd = path
  139.             return
  140.         
  141.         if path == '..':
  142.             dirs = self._cwd.split('/')
  143.             print dirs
  144.             self._cwd = u'/'.join(dirs)
  145.         else:
  146.             self._cwd += path
  147.         
  148.         
  149.     def open(self, filename, mode):
  150.         
  151.         image_id = filename.split('.')[0]
  152.         query = u"select id,data from image where id = %(image_id)s"
  153.         row = execute(query, {'image_id':image_id})[0]
  154.         
  155.         # nameプロパティが必要なのでラップする
  156.         class FileWrapper(object):
  157.             def __init__(self, row):
  158.                 self.file = StringIO()
  159.                 self.file.write(row['data'])
  160.                 self.file.seek(0)
  161.                     
  162.                 self.name = u'%(id)s.jpg' % row
  163.             def __getattr__(self, attr):
  164.                 return getattr(self.file, attr)
  165.         return FileWrapper(row)
  166.     
  167.     
  168. def execute(query, params):
  169.     con = MySQLdb.connect(
  170.         host='192.168.1.110',
  171.         db='ftp',
  172.         user='admin',
  173.         passwd='P@ssw0rd',
  174.         charset='utf8')
  175.     cur = con.cursor(MySQLdb.cursors.DictCursor)
  176.     
  177.     cur.execute(query, params)
  178.     
  179.     rows = []
  180.     for row in cur:
  181.         rows.append(row)
  182.     cur.close()
  183.     con.close()
  184.     
  185.     return rows
  186. def main():
  187.     
  188.     # 認証用のオブジェクトを作成
  189.     authorizer = MyAuthorizer()
  190.     #authorizer = DummyAuthorizer()
  191.     # id:user,pass:12345というユーザーを作成 操作権限(perm)は全て与える
  192.     #authorizer.add_user('symfo', 'pass', '.', perm='elradfmwM')
  193.     # FTP通信のハンドラーを作成 ユーザー認証オブジェクトを設定する
  194.     handler = FTPHandler
  195.     handler.authorizer = authorizer
  196.     # 接続時に表示される文字列指定
  197.     handler.banner = "pyftpdlib based ftpd ready."
  198.     
  199.     # 使用するファイルシステム
  200.     handler.abstracted_fs = MyFileSystem
  201.     # ftpサーバーのポートを指定
  202.     address = ('localhost', 10021)
  203.     server = FTPServer(address, handler)
  204.     # サーバー起動
  205.     server.serve_forever()
  206. if __name__ == '__main__':
  207.     main()




狙い通りの表示になっています。

528_01.png


ファイルのダウンロードもちゃんと行えました。
関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2015/01/18(日) 14:00:26|
  2. Python
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<Python + pyftpdlibでカスタムFTPサーバーを作成5 ファイルのアップロード | ホーム | Python + pyftpdlibでカスタムFTPサーバーを作成3 ファイルの取得>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/1586-5b2bd3e4
この記事にトラックバックする(FC2ブログユーザー)