アップロードしたファイルのサイズを制限するtwistedを使用してFTPサーバーを実装しようとしています。理想的には、転送が開始される前にこれが行われるのが理想ですが、大きすぎると転送中に正常に終了すると問題にはなりません。Twisted FTPサーバーのファイルサイズを制限する
私は非常に基本的なftpserver.pyから始め、ゆっくりと基礎的なクラスをftp.pyから引き出し、内部のものを取得しています。
現在のコードは、「ハックアンドスラッシュ」スタイルを採用しています。
#!/usr/bin/python
import os
from twisted.protocols.ftp import FTPFactory, FTPShell, FTPAnonymousShell, IFTPShell
from twisted.cred.portal import Portal
from twisted.cred.checkers import AllowAnonymousAccess
from twisted.internet import reactor, defer
from twisted.python import filepath, failure
class FileConsumer1(object):
def __init__(self, fObj):
self.fObj = fObj
def registerProducer(self, producer, streaming):
self.producer = producer
assert streaming
def unregisterProducer(self):
self.producer = None
self.fObj.close()
def write(self, bytes):
size = os.fstat(self.fObj.fileno()).st_size + len(bytes)
if size > 10:
raise Exception("File too large") # WHAT GOES HERE?
self.fObj.write(bytes)
class FileWriter1(object):
def __init__(self, fObj):
self.fObj = fObj
self._receive = False
def receive(self):
assert not self._receive, "Can only call IWriteFile.receive *once* per instance"
self._receive = True
return defer.succeed(FileConsumer1(self.fObj))
def close(self):
return defer.succeed(None)
class FTPShell1(FTPShell):
def openForWriting(self, path):
p = self._path(path)
if p.isdir():
return defer.fail(IsADirectoryError(path))
try:
fObj = p.open('w')
except (IOError, OSError), e:
return errnoToFailure(e.errno, path)
except:
return defer.fail()
return defer.succeed(FileWriter1(fObj))
class FTPRealm1(object):
def __init__(self, root):
self.path = filepath.FilePath(root)
def requestAvatar(self, avatarId, mind, *interfaces):
avatar = FTPShell1(self.path)
return (IFTPShell, avatar, getattr(avatar, 'logout', lambda: None))
p = Portal(FTPRealm1('./'), [ AllowAnonymousAccess() ])
f = FTPFactory(p)
reactor.listenTCP(4021, f)
reactor.run()
明らかに、サイズ> 10の方が大きいかどうかを確認しますが、この時点で問題があることを示す必要がありますか?それがそのままで、ねじれはその例外をキャッチしますが、あまりエレガントではありません。私がftp.pyの検査から見る限り、私がここに戻ることは何も明らかではありません。何らかの方法で繰延べることはできますか?私はどのようにエレガントに転送を終了する必要がありますか?
おかげで、
はここで改訂版FileConsumer内に受信したデータを蓄積し
#!/usr/bin/python
import os
from zope.interface import Interface, implements
from twisted.protocols.ftp import FTPFactory, FTPShell, FTPAnonymousShell, IFTPShell, IWriteFile , BaseFTPRealm, FTPCmdError, EXCEEDED_STORAGE_ALLOC
from twisted.cred.portal import Portal
from twisted.cred.checkers import AllowAnonymousAccess
from twisted.internet import reactor, defer, interfaces
from twisted.python import filepath
class ExceededStorageAllocError(FTPCmdError):
errorCode = EXCEEDED_STORAGE_ALLOC
class FileConsumer(object):
implements(interfaces.IConsumer)
def __init__(self):
self.data = ""
self.error = None
def registerProducer(self, producer, streaming):
self.producer = producer
assert streaming
def unregisterProducer(self):
if self.producer:
self.producer.stopProducing()
self.producer = None
def write(self, bytes):
self.data += bytes
if len(self.data) > 10:
self.unregisterProducer()
self.error = ExceededStorageAllocError()
class FileWriter(object):
implements(IWriteFile)
def __init__(self, path):
self.path = path
def receive(self):
self.consumer = FileConsumer()
return defer.succeed(self.consumer)
def close(self):
if self.consumer.error:
return defer.fail(self.consumer.error)
try:
f = self.path.open('w')
except (IOError, OSError), e:
return errnoToFailure(e.errno, path)
f.write(self.consumer.data)
return defer.succeed(None)
class FTPShell1(FTPShell):
makeDirectory = FTPAnonymousShell.makeDirectory
removeDirectory = FTPAnonymousShell.removeDirectory
def openForWriting(self, path):
p = self._path(path)
if p.isdir():
return defer.fail(IsADirectoryError(path))
return defer.succeed(FileWriter(p))
class FTPRealm1(BaseFTPRealm):
def __init__(self, root):
self.root = root
def requestAvatar(self, avatarId, mind, *interfaces):
avatar = FTPShell1(filepath.FilePath(self.root))
return (IFTPShell, avatar, getattr(avatar, 'logout', lambda: None))
p = Portal(FTPRealm1('./'), [ AllowAnonymousAccess() ])
f = FTPFactory(p)
reactor.listenTCP(4021, f)
reactor.run()
()ファイルが長すぎる場合は、その後中止します。 FileWriter()のclose()メソッドは、そのエラーを報告するか、完全バッファをファイルに書き込みます。
私はこれを持ってるだけで本当の問題は、実行したときに、例外がサーバー上に表示されていることです:ツイストのプロデューサ/コンシューマモデルで、私は迅速免責事項として
Unexpected error received during transfer:
Traceback (most recent call last):
Failure: __main__.ExceededStorageAllocError:
こんにちは、私はしばらくの間ツイストを使用しているが、アイデアのおかげで、これは私がプロデューサー/消費者モデルに接触して初めてです。私はあなたの提案を試みた、FileConsumer1()クラスunregisterCosumer()メソッドを持っていない - unregisterProducer()を意味するのですか? –
うん、はい、私は 'unregisterProducer'を意味しました。答えも編集しました。 –
あなたの提案を使用して修正された実装のための私の2番目のバージョンを参照してください。 –