Python ftplib:以写入模式打开文件?
Python ftplib: open file in write mode?
如何以写入模式打开 FTP 服务器上的文件?我知道我可以 write/create 直接归档(当我有数据时),但我想先打开它进行写入,然后才像在本地使用 contextmanager 那样写入它。
原因是,我想创建具有统一方法的接口来与传输协议服务器一起工作。特别是 SFTP 和 FTP.
因此使用 SFTP 很容易(使用 paramiko
):
def open(sftp, fname, mode='r'):
return sftp.open(fname, mode=mode)
现在我可以做到了:
with open(sftp, 'some_file.txt', 'w') as f:
f.write(data)
然后我可以阅读写的内容
with open(sftp, 'some_file.txt', 'r') as f:
print(f.read().decode('utf-8'))
如何为 FTP 执行相同的实现(使用 ftplib
)?
阅读 FTP 的部分,我能够实现并且可以像 SFTP 一样以阅读模式打开文件。但是我怎样才能以写入模式打开它呢? ftplib 方法 storbinary
要求提供数据 "immediately"。我的意思是我应该已经通过 open
方法传递了我想写的数据(但这样会破坏统一方法的目的)?
import io
def open(ftp, filename, mode='r'):
"""Open a file on FTP server."""
def handle_buffer(buffer_data):
bio.write(buffer_data)
# Reading implementation
if mode == 'r':
bio = io.BytesIO()
ftp.retrbinary(
'RETR %s' % filename, callback=handle_buffer)
bio.seek(0)
return bio
# Writing implementation.
if mode == 'w':
# how to open in write mode?
更新
假设我们在 FTP:
中有立即写入实现
bio = io.BytesIO
# Write some data
data = csv.writer(bio)
data.writerows(data_to_export)
bio.seek(0)
# Store. So it looks like storbinary does not open file in w mode, it does everything in one go?
ftp.storbinary("STOR " + file_name, sio)
所以问题是如何将写入数据与仅以写入模式打开文件分开。甚至可以使用 ftplib 吗?
所以经过一番努力,我能够完成这项工作。解决方案是在读取时为 open
方法实现自定义上下文管理器(必须重新实现读取模式,因为它仅适用于普通文件读取,但如果说我会尝试使用 csv reader 则失败) 模式和写入模式。
对于读取模式,我选择使用tempfile,因为使用其他方法,我无法使用不同的readers(普通文件reader、csv reader ETC。)。尽管在读取模式下使用打开的临时文件时,一切都按预期工作。
对于写入模式,我能够利用内存缓冲区 -> io.BytesIO。所以对于写作来说,没有必要使用临时文件。
import tempfile
class OpenRead(object):
def _open_tempfile(self):
self.tfile = tempfile.NamedTemporaryFile()
# Write data on tempfile.
self.ftp.retrbinary(
'RETR %s' % self.filename, self.tfile.write)
# Get back to start of file, so it would be possible to
# read it.
self.tfile.seek(0)
return open(self.tfile.name, 'r')
def __init__(self, ftp, filename):
self.ftp = ftp
self.filename = filename
self.tfile = None
def __enter__(self):
return self._open_tempfile()
def __exit__(self, exception_type, exception_value, traceback):
# Remove temporary file.
self.tfile.close()
class OpenWrite(object):
def __init__(self, ftp, filename):
self.ftp = ftp
self.filename = filename
self.data = ''
def __enter__(self):
return self
def __exit__(self, exception_type, exception_value, traceback):
bio = io.BytesIO()
if isinstance(self.data, six.string_types):
self.data = self.data.encode()
bio.write(self.data)
bio.seek(0)
res = self.ftp.storbinary('STOR %s' % self.filename, bio)
bio.close()
return res
def write(self, data):
self.data += data
def open(ftp, filename, mode='r'):
"""Open a file on FTP server."""
if mode == 'r':
return OpenRead(ftp, filename)
if mode == 'w':
return OpenWrite(ftp, filename)
P.S。如果没有上下文管理器,这可能无法正常工作,但现在它对我来说是好的解决方案。如果谁有更好的实现,欢迎分享。
更新
决定使用 ftputil
包而不是标准 ftplib
。所以不需要所有这些黑客攻击,因为 ftputil
会处理它并且它实际上使用许多与 paramiko
相同的命名方法,做同样的事情,所以统一协议使用要容易得多。
如何以写入模式打开 FTP 服务器上的文件?我知道我可以 write/create 直接归档(当我有数据时),但我想先打开它进行写入,然后才像在本地使用 contextmanager 那样写入它。
原因是,我想创建具有统一方法的接口来与传输协议服务器一起工作。特别是 SFTP 和 FTP.
因此使用 SFTP 很容易(使用 paramiko
):
def open(sftp, fname, mode='r'):
return sftp.open(fname, mode=mode)
现在我可以做到了:
with open(sftp, 'some_file.txt', 'w') as f:
f.write(data)
然后我可以阅读写的内容
with open(sftp, 'some_file.txt', 'r') as f:
print(f.read().decode('utf-8'))
如何为 FTP 执行相同的实现(使用 ftplib
)?
阅读 FTP 的部分,我能够实现并且可以像 SFTP 一样以阅读模式打开文件。但是我怎样才能以写入模式打开它呢? ftplib 方法 storbinary
要求提供数据 "immediately"。我的意思是我应该已经通过 open
方法传递了我想写的数据(但这样会破坏统一方法的目的)?
import io
def open(ftp, filename, mode='r'):
"""Open a file on FTP server."""
def handle_buffer(buffer_data):
bio.write(buffer_data)
# Reading implementation
if mode == 'r':
bio = io.BytesIO()
ftp.retrbinary(
'RETR %s' % filename, callback=handle_buffer)
bio.seek(0)
return bio
# Writing implementation.
if mode == 'w':
# how to open in write mode?
更新
假设我们在 FTP:
中有立即写入实现bio = io.BytesIO
# Write some data
data = csv.writer(bio)
data.writerows(data_to_export)
bio.seek(0)
# Store. So it looks like storbinary does not open file in w mode, it does everything in one go?
ftp.storbinary("STOR " + file_name, sio)
所以问题是如何将写入数据与仅以写入模式打开文件分开。甚至可以使用 ftplib 吗?
所以经过一番努力,我能够完成这项工作。解决方案是在读取时为 open
方法实现自定义上下文管理器(必须重新实现读取模式,因为它仅适用于普通文件读取,但如果说我会尝试使用 csv reader 则失败) 模式和写入模式。
对于读取模式,我选择使用tempfile,因为使用其他方法,我无法使用不同的readers(普通文件reader、csv reader ETC。)。尽管在读取模式下使用打开的临时文件时,一切都按预期工作。
对于写入模式,我能够利用内存缓冲区 -> io.BytesIO。所以对于写作来说,没有必要使用临时文件。
import tempfile
class OpenRead(object):
def _open_tempfile(self):
self.tfile = tempfile.NamedTemporaryFile()
# Write data on tempfile.
self.ftp.retrbinary(
'RETR %s' % self.filename, self.tfile.write)
# Get back to start of file, so it would be possible to
# read it.
self.tfile.seek(0)
return open(self.tfile.name, 'r')
def __init__(self, ftp, filename):
self.ftp = ftp
self.filename = filename
self.tfile = None
def __enter__(self):
return self._open_tempfile()
def __exit__(self, exception_type, exception_value, traceback):
# Remove temporary file.
self.tfile.close()
class OpenWrite(object):
def __init__(self, ftp, filename):
self.ftp = ftp
self.filename = filename
self.data = ''
def __enter__(self):
return self
def __exit__(self, exception_type, exception_value, traceback):
bio = io.BytesIO()
if isinstance(self.data, six.string_types):
self.data = self.data.encode()
bio.write(self.data)
bio.seek(0)
res = self.ftp.storbinary('STOR %s' % self.filename, bio)
bio.close()
return res
def write(self, data):
self.data += data
def open(ftp, filename, mode='r'):
"""Open a file on FTP server."""
if mode == 'r':
return OpenRead(ftp, filename)
if mode == 'w':
return OpenWrite(ftp, filename)
P.S。如果没有上下文管理器,这可能无法正常工作,但现在它对我来说是好的解决方案。如果谁有更好的实现,欢迎分享。
更新
决定使用 ftputil
包而不是标准 ftplib
。所以不需要所有这些黑客攻击,因为 ftputil
会处理它并且它实际上使用许多与 paramiko
相同的命名方法,做同样的事情,所以统一协议使用要容易得多。