FTPserver
客户端代码:
import os
import hashlib BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) FILE_PATH = os.path.join(BASE_DIR, 'donwload') def make_md5_on_file(file_path):
'''给文件制作md5 每次都要打开文件,要想办法保存到一个位置,当文件发生修改时更新这个md5'''
m = hashlib.md5()
with open(file_path, 'rb') as f:
for line in f:
bytes_str = line
m.update(bytes_str)
md5value = m.hexdigest()
return md5value
import socket
import optparse
import json
import struct
import os
import sys
import shelve
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import settings class FTPClient(object):
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
allow_reuse_address = True
max_pack_size = 8192
request_queue_size = 5
coding = 'utf-8'
donwload_dir = settings.FILE_PATH def __init__(self, connect=True):
''' elf.options 这是这个模块生成的一个字典 self.args 这是用户输入的命令。和sys.argv 差不多
如果用户按照规则输入,那么相应的值 就会在字典中显示, 不按规则输入的值,就放倒了列表中'''
parser = optparse.OptionParser()
parser.add_option("-s", "--server", dest='server', help="ftp server ip_addr")
parser.add_option("-P", "--port", type="int", dest="port", help="ftp server port")
parser.add_option("-u", "--username", dest="username", help="username info")
parser.add_option("-p", "--password", dest="password", help="password info")
self.options, self.args = parser.parse_args()
self.current_dir = None
self.argv_verification() # 判断合法性
self.socket = self.make_connection() # 建立连接
self.shelve_obj = shelve.open('defect_file') # 得到下载时shelve文件对象
self.shelve_load_obj = shelve.open('unfinish_file')
if connect:
try:
self.client_connect()
except Exception:
self.client_close()
exit('以断开连接') def argv_verification(self):
'''判断用户输入的合法性'''
if not self.options.server or not self.options.port:
exit('必须提供,ip 和 端口') def make_connection(self):
'''由构造函数调用生成 socket 对象'''
self.socket = socket.socket(self.address_family, self.socket_type)
return self.socket def client_connect(self):
'''由构造函数直接调用,激活客户端,连接服务端'''
self.socket.connect((self.options.server, self.options.port)) # def client_close(self):
'''由构造函数调用,关闭客户端'''
self.socket.close() def auth(self):
'''输入账户名,密码。服务端验证完成后,根据接收到的状态码,返回 Ture or False'''
count = 0
while count < 3:
username = input('username:').strip()
if not username: continue
self.current_dir = '\\' + username
pwd = input('password:').strip()
cmd = {
'action_type': 'auth',
'username': username,
'password': pwd
} self.send_head(cmd)
head_dic = self.recv_head()
if head_dic['status_code'] == 200: return True
else:
count += 1
print(head_dic['status_msg'])
continue def send_head(self, cmd):
'''发送客户端的报头,'''
client_head_bytes = json.dumps(cmd).encode(self.coding)
client_head_len = struct.pack('i', len(client_head_bytes))
self.socket.send(client_head_len)
self.socket.send(client_head_bytes) def recv_head(self):
'''接收服务端发来的报头'''
obj = self.socket.recv(4)
header_size = struct.unpack('i', obj)[0]
header_json = self.socket.recv(header_size).decode(self.coding)
header_dict = json.loads(header_json)
return header_dict def send_file(self, file_path, head_dic, sercver_dir, md5val, send_size=0):
'''发送文件'''
self.shelve_load_obj[file_path] = [head_dic, sercver_dir, md5val]
genertor = self.progress_bar(head_dic['filesize'])
genertor.__next__()
with open(file_path, 'rb') as f:
f.seek(send_size)
for line in f:
self.socket.send(line)
send_size += len(line)
genertor.send(send_size)
else:
del self.shelve_load_obj[file_path]
print('数据发送成功')
header_dict = self.recv_head()
print(header_dict['status_msg']) def recv_file(self, file_path, file_size, md5_value, filename, recv_size=0):
'''接收服务端文件数据, 并 保存所有文件信息, 防止程序中断时,可以进行续传,
文件正常传完, 删除文件描述信息, 并改名'''
file_abs_path = os.path.join(r'%s\%s' % (self.current_dir, filename)) # 拼接客户端在那个位置下载的文件
self.shelve_obj[file_abs_path] = [file_size, filename+'.download'] # 以绝对路径为键,保存文件大小的值 with open(file_path, 'ab') as f:
while recv_size < file_size:
line = self.socket.recv(self.max_pack_size)
f.write(line)
recv_size += len(line)
progres(recv_size, file_size) # 进度条
md5value = settings.make_md5_on_file(file_path)
if md5value == md5_value:
del self.shelve_obj[file_abs_path]
if filename not in os.listdir(self.donwload_dir):
os.renames(file_path, os.path.join(self.donwload_dir, filename))
else:
print('文件重名,未覆盖!', end='')
print() def put(self, *args):
'''上传文件'''
cmd = args[0]
filename = args[1]
sercver_dir = '%s\%s' % (self.current_dir, filename)
if not os.path.isfile(os.path.join(self.donwload_dir, filename)):
print('file %s 不在donwload文件中' % filename)
return
else:
filesize = os.path.getsize(os.path.join(self.donwload_dir, filename))
md5value = settings.make_md5_on_file(os.path.join(self.donwload_dir, filename))
head_dic = {'action_type': cmd, 'filesize': filesize, 'filename': filename, 'md5value': md5value}
file_path = os.path.join(self.donwload_dir, filename)
self.send_head(head_dic)
self.send_file(file_path, head_dic, sercver_dir, md5value) def re_put(self, file_path, load_head_dic, sercver_dir, md5value):
server_head_dic = self.recv_head()
received_size = server_head_dic['file_size']
self.send_file(file_path, load_head_dic, sercver_dir, md5value, send_size=received_size) def progress_bar(self, total_size):
'''生成器 方式的, 进度条展示'''
current_percent = 0
last_percent = 0
while True:
recvived_size = yield current_percent
current_percent = int(recvived_size / total_size * 100)
if current_percent > last_percent:
print("#" * int(current_percent/2) + "{percent}%".format(percent=current_percent), flush=True, end='\r')
last_percent = current_percent def get(self, *args):
'''下载文件'''
cmd = args[0]
filename = args[1]
client_head_dic = {'action_type': cmd, 'filename': filename}
self.send_head(client_head_dic)
server_head_dic = self.recv_head() if server_head_dic['status_code'] == 300:
print(server_head_dic['status_msg'])
return file_path = os.path.join(self.donwload_dir, '%s.download' % filename) # 存放文件路径
total_size = server_head_dic['filesize'] # 文件最大大小
md5_val = server_head_dic['md5value'] # 得到服务端发来的 MD5 值
self.recv_file(file_path, total_size, md5_val, filename) def re_get(self, head_dic):
'''由程序自检之后,用户选择 是否进行续传。 如果续传 调用该方法。
发送报头:
1. 检测 已收到文件大小
2. 编辑报头,使用 self.socket.send() 发送报头
3. 接收文件,'''
self.send_head(head_dic)
server_head_dic = self.recv_head()
if server_head_dic['status_code'] == 300:
print(server_head_dic['status_msg'])
return
file_path = os.path.join(self.donwload_dir, head_dic['filename']) # 存放文件路径
total_size = head_dic['total_size']
md5_val = server_head_dic['md5value'] # 得到服务端发来的 MD5 值
self.recv_file(file_path, total_size, md5_val, server_head_dic['filename'], head_dic['received_file_size']) def ls(self, *args):
'''显示当前目录下的文件'''
cmd = args[0]
client_head_dic = {'action_type': cmd}
self.send_head(client_head_dic)
server_head_dic = self.recv_head()
if server_head_dic['status_code'] == 0:
print(server_head_dic['dir_info'])
if server_head_dic['status_code'] == 100:
print(server_head_dic['dir_info']) def cd(self, *args):
'''切换目录'''
cmd = args[0]
dirname = args[1]
if dirname != '.':
client_head_dic = {'action_type': cmd, 'dirname': dirname}
self.send_head(client_head_dic)
server_head_dic = self.recv_head()
if server_head_dic['status_code'] == 400:
print(server_head_dic['status_msg'])
elif server_head_dic['status_code'] == 401:
print(server_head_dic['status_msg'])
self.current_dir = server_head_dic['dirn']
elif server_head_dic['status_code'] == 0:
self.current_dir = server_head_dic['dirn']
else:
print('输入错误')
return def help_msg(self, *args):
msg = '''command error!!!
Correct format:
get filename 下载文件
put filename 上传文件
ls 显示当前所在目录下的文件和子目录
cd dirname 切换到那个目录下'''
print(msg) def unfinished_file_check(self):
if not self.shelve_obj:
print('没有未接收的文件'.center(30, '-'))
return
for index, abs_file in enumerate(self.shelve_obj.keys()):
self.received_file_size = os.path.getsize(os.path.join(self.donwload_dir, self.shelve_obj[abs_file][1]))
print('文件编号 %d 服务器文件保存地址 %s 文件总大小%s 文件名%s 已收到文件大小%s' %
(index, abs_file, self.shelve_obj[abs_file][0], self.shelve_obj[abs_file][1], self.received_file_size))
while True:
choice = input('选择想要继续下载的文件编号 [back] 退出:').strip()
if not choice: continue
if choice == 'back': break
if choice.isdigit():
choice = int(choice)
if choice >=0 and choice <= index:
selected_file = list(self.shelve_obj.keys())[choice] # 通过索引,拿到想要的那个文件的 key
total_size = self.shelve_obj[selected_file][0] # 文件总大小
file_name = self.shelve_obj[selected_file][1] # 文件名
size = self.received_file_size # 已收到的文件大小
head_dic = {'action_type': 're_get', 'abs_file': selected_file, 'total_size': total_size,
'received_file_size': size, 'filename': file_name}
self.re_get(head_dic) def unsend_by_file_check(self):
if not self.shelve_load_obj:
print('没有未上传完整的文件'.center(30, '-'))
return
for index, abs_file in enumerate(self.shelve_load_obj.keys()):
print('%s %s %s' % (index, abs_file, self.shelve_load_obj[abs_file]))
while True:
choice = input('选择想要继续上传的文件编号 [back] 退出:').strip()
if not choice: continue
if choice == 'back': break
if choice.isdigit():
choice = int(choice)
if choice >= 0 and choice <= index:
# file_path, head_dic, sercver_dir, md5val
file_path = list(self.shelve_load_obj.keys())[choice]
filesize = self.shelve_load_obj[file_path][0]['filesize']
load_head_dic = self.shelve_load_obj[file_path][0]
sercver_dir = self.shelve_load_obj[file_path][1].strip('\\')
md5value = self.shelve_load_obj[file_path][2]
head_dic = {'action_type': 're_put', 'abs_file': sercver_dir, 'md5value': md5value, 'filesize': filesize}
self.send_head(head_dic)
self.re_put(file_path, load_head_dic, sercver_dir, md5value) def interactive(self): # 交互函数
'''处理 与 服务端 的所有交互,解析用户输入的指令'''
if self.auth():
self.unfinished_file_check()
self.unsend_by_file_check()
while True:
inp = input('[%s] Q退出:' % self.current_dir).strip()
if not inp: continue
cmds = inp.split()
if inp != 'Q':
if hasattr(self, cmds[0]):
func = getattr(self, cmds[0])
func(*cmds)
else:
self.help_msg()
continue
else:
self.shelve_obj.close()
self.client_close()
exit('谢谢使用') def make_dir(self, *args):
'''在自己的目录下,创建文件夹'''
print('在自己的目录下,创建文件夹') def humanbytes(B):
'''这传代码 抄来的 T_T '''
B = float(B)
KB = float(1024)
MB = float(KB ** 2) # 1,048,576
GB = float(KB ** 3) # 1,073,741,824
TB = float(KB ** 4) # 1,099,511,627,776 if B < KB:
return '{0} {1}'.format(B,'Bytes' if 0 == B > 1 else 'Byte')
elif KB <= B < MB:
return '{0:.2f} KB'.format(B/KB)
elif MB <= B < GB:
return '{0:.2f} MB'.format(B/MB)
elif GB <= B < TB:
return '{0:.2f} GB'.format(B/GB)
elif TB <= B:
return '{0:.2f} TB'.format(B/TB) def progres(recv_size, total_size):
bar_length = 50
percent = float(recv_size) / float(total_size)
hashes = '=' * int(percent * bar_length)
spaces = ' ' * (bar_length - len(hashes)) sys.stdout.write("\r传输中: [%s] %d%% %s/%s " % (hashes + spaces, percent * 100,
humanbytes(recv_size), humanbytes(total_size)))
sys.stdout.flush() if __name__ == '__main__':
client = FTPClient()
client.interactive()
client.py
服务端代码:
import os
import sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 入口程序必须先 指定 基础目录。以便于其余的调用程序,可以通过这个路径,进行相对的导入
sys.path.append(BASE_DIR) # 将基础目录添加到,路径列表中 if __name__ == '__main__':
from core import management
# print()
# ['luffy_server.py', 'start']
# sys.argv 把用户终端输入的命令,拿到并把拿到的列表,交给 解析命令的类。传给__init__了
#['luffy_server.py', 'start']
argv_parser = management.ManagementTool(sys.argv)
argv_parser.execute()
import os
import socket BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) HOST = "127.0.0.1" # 服务端IP 用于服务端进行绑定使用
PORT = 8080 # 服务端 端口 ADDRESS_FAMILY = socket.AF_INET
SOCKET_TYPE = socket.SOCK_STREAM
ALLOW_REUSE_ADDRESS = True
MAX_PACKET_SIZE = 8192
MAX_SOCKET_LISTEN = 5
CODING = 'utf-8' # 指定编码类型
USER_HOME_DIR = os.path.join(BASE_DIR, 'home') ACCOUNT_FILE = os.path.join(BASE_DIR, 'conf', 'accounts.ini')
settings
import hashlib
import configparser
from .import settings def make_md5_on_file(file_path):
'''给文件制作md5 每次都要打开文件,要想办法保存到一个位置,当文件发生修改时更新这个md5'''
m = hashlib.md5()
with open(file_path, 'rb') as f:
for line in f:
bytes_str = line
m.update(bytes_str)
md5value = m.hexdigest()
return md5value def load_all_user_info():
'''由FTPserver调用,加载所有的用户数据到内存'''
confing = configparser.ConfigParser()
confing.read(settings.ACCOUNT_FILE)
return confing
conf_tool.py
from core import main class ManagementTool(object):
'''负责对 用户输入的指令,进行解析。并调用相应的模块处理'''
def __init__(self, sys_argv):
self.sys_argv = sys_argv
self.verification() def verification(self):
'''由构造函数调用 验证用户输入的指令是否合法,如果不合法。打印帮助信息'''
if len(self.sys_argv) < 2: # sys.argv 列表中默认会带上文件名,所以长度必须不能小于2
self.help_msg()
cmd = self.sys_argv[1]
if not hasattr(self, cmd):
print('无效语法')
self.help_msg() def help_msg(self):
msg = '''
start start FTP server
stop stop FTP server
restart restart FTP server
createuser username create a ftp user
其余功能还没扩展呢'''
exit(msg) # 如果命令输入的是错误的,就退出程序。因为 如果继续向后走的话,会因为没有这个命令报错。
# 虽然可以,加上循环。让用户继续输入。不过 再来一次就好了。没必要那么麻烦 def execute(self):
'''进行解析并执行指令,写在这里可以进行扩展'''
cmd = self.sys_argv[1]
func = getattr(self, cmd)
func() # 这里并没有传参数,因为sys.argv 是构造函数中的,相当于类中的一个全局变量。
# 其余函数 直接调用就好了,不需要还要在这里传参数 def start(self):
'''启动FTP server'''
server = main.FTPServer(self) # FTPServer 有可能用到当前这个程序的一些东西,那么就把实例本身
server.run_forever() # 当作一个参数 传给FTPServer 那就可以在FTPServer中使用这个类中的属性了 def stop(self):
'''这是停止服务'''
print('停止服务了') def restart(self):
'''重新启动服务器'''
print('重新启动服务器') def createuser(self):
'''管理员创建用户使用的'''
print('管理员专用')
management.py
import socket
import json
import hashlib
import struct
import os
import subprocess
from conf import settings
from conf import config_tool class FTPServer(object):
'''处理与客户端所有的交互的 socket server
所需要的参数,从settings 和 实例化时 传进来'''
address_family = settings.ADDRESS_FAMILY
socket_type = settings.SOCKET_TYPE
allow_reuse_address = settings.ALLOW_REUSE_ADDRESS # 是否重用端口开关 默认 Fales
max_packet_size = settings.MAX_PACKET_SIZE # 最大传输流量8192
coding = settings.CODING # utf-8
request_queue_size = settings.MAX_SOCKET_LISTEN # 最大挂起数量5
server_dir = settings.USER_HOME_DIR # 总家目录 STATUS_CODE = {
0: 'normal ',
100: 'ls Error info',
101: 'current dir has no file at all',
200: 'Username Password Authentication Successful!',
201: 'wrong username or password',
300: 'The file was not find',
301: 'find the file',
302: 'The server did not receive the complete data',
303: 'The server receive the complete data',
400: 'The dirname was not find',
401: 'Has entered this directory',
500: "It's already on the top floor"
} def __init__(self, management_instance, bind_and_acttivate=True): # 在management类中start的位置,将management的实例传进来
'''构造函数,可扩展 不可覆盖'''
self.management_instance = management_instance # 类的构造函数中,接收这个参数,这样就能够使用到传进来的实例的对象中的属性
self.socket = socket.socket(self.address_family, self.socket_type)
self.config_obj = config_tool.load_all_user_info() #
if bind_and_acttivate:
try:
self.server_bind()
self.server_activate()
except Exception:
self.server_close() def server_bind(self):
"""由构造函数调用以绑定套接字"""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((settings.HOST, settings.PORT))
self.server_address = self.socket.getsockname() def server_activate(self):
"""由构造函数调用以激活服务器 """
self.socket.listen(self.request_queue_size) def get_request(self):
"""从套接字获取请求和客户机地址。 """
return self.socket.accept() def server_close(self):
"""调用以清理服务器。"""
self.socket.close() def close_request(self):
"""调用以清除单个请求"""
self.request.close() def run_forever(self):
'''启动socket server 运行到天荒地老'''
print('starting luffyFTP server on %s:%s'.center(50, '-') % (settings.HOST, settings.PORT))
while True:
try:
self.request, self.client_addr = self.get_request()
print('from client %s' % (self.client_addr,))
# print(self.request, self.cline_addr)
self.handle()
except Exception:
print('客户端 %s 断开连接' % (self.client_addr,))
self.close_request() def handle(self):
'''接收客户端发来的报头 解析之后 进行相应的操作'''
while True:
raw_data = self.request.recv(4)
if not raw_data:
print('client %s disconnection' % self.client_addr)
del self.request, self.client_addr
break
data_len = struct.unpack('i', raw_data)[0]
date_json = self.request.recv(data_len).decode(self.coding)
data_dic = json.loads(date_json)
action_type = data_dic.get('action_type')
if action_type: # 不能为空
if hasattr(self, "_%s" % action_type):
func = getattr(self, "_%s" % action_type)
func(data_dic) def send_response(self, status_code, *args, **kwargs):
'''打包发送状态码消息给客户端'''
data = kwargs
data['status_code'] = status_code
data['status_msg'] = self.STATUS_CODE[status_code]
bytes_data = json.dumps(data).encode('utf-8')
head_struct = struct.pack('i', len(bytes_data)) self.request.send(head_struct) # 这里时通信循环干的事情了,一定要是self.request
self.request.send(bytes_data) def authentication(self, username, password):
'''对用户名密码进行验证'''
if username in self.config_obj:
_password = self.config_obj[username]['password']
md5_obj = hashlib.md5()
md5_obj.update(password.encode('utf-8'))
if md5_obj.hexdigest() == _password:
return True def _auth(self, data):
'''处理用户认证请求'''
if self.authentication(data.get('username'), data.get('password')):
self.home_dir = os.path.join(self.server_dir, data.get('username'))
self.userhome_dir = os.path.join(self.server_dir, data.get('username'))
self.send_response(status_code=200,)
else:
self.send_response(status_code=201) def _put(self, data):
'''用户进行上传,'''
file_path = os.path.normpath(os.path.join(self.userhome_dir, data['filename']))
filesize = data['filesize']
cilent_md5 = data['md5value']
result = self.recv_file(file_path, cilent_md5, filesize)
if result:
self.send_response(status_code=303)
else:
self.send_response(status_code=302) def _get(self, data):
'''用户进行下载'''
file_path = os.path.normpath(os.path.join(self.userhome_dir, data['filename']))
if not os.path.isfile(file_path):
self.send_response(status_code=300)
else:
filesize = os.path.getsize(file_path)
md5value = config_tool.make_md5_on_file(file_path)
self.send_response(status_code=301, filesize=filesize, md5value=md5value, filename=data['filename'])
self.send_file(file_path) def recv_file(self, file_path, cilent_md5, filesize, recv_size=0):
'''接收文件,需要文件存放路径,MD5值,文件大小, 已接受的文件大小''' with open(file_path, 'ab') as f:
while recv_size < filesize:
recv_data = self.request.recv(self.max_packet_size)
if not recv_data: break
f.write(recv_data)
recv_size += len(recv_data)
md5value = config_tool.make_md5_on_file(file_path)
if md5value == cilent_md5:
return True def send_file(self, file_path, send_size=0):
with open(file_path, 'rb') as f:
f.seek(send_size)
for line in f:
self.request.send(line) def _re_get(self, data):
abs_file = data['abs_file'].strip('\\')
file_path = os.path.normpath(os.path.join(settings.USER_HOME_DIR, abs_file))
if not os.path.isfile(file_path):
self.send_response(status_code=300)
if os.path.getsize(file_path) != data['total_size']:
self.send_response(status_code=300)
else:
md5value = config_tool.make_md5_on_file(file_path)
self.send_response(status_code=301, md5value=md5value, filename=os.path.basename(file_path))
self.send_file(file_path, send_size=data['received_file_size']) def _re_put(self, data):
'''用于用户上传,没有传完整,续传使用'''
abs_file = data['abs_file'].strip('\\')
file_path = os.path.normpath(os.path.join(settings.USER_HOME_DIR, abs_file))
md5value = data['md5value'] if not os.path.isfile(file_path):
self.send_response(status_code=300)
else:
received_file_size = os.path.getsize(file_path) # 返回给客户端,已经收到了多少字节
self.send_response(status_code=301, file_size=received_file_size)
filesize = data['filesize'] - received_file_size
result = self.recv_file(file_path, md5value, filesize)
if result:
self.send_response(status_code=303)
else:
self.send_response(status_code=302) def _ls(self, data):
'''给客户端显示当前文件下有哪些内容'''
cmd_boj = subprocess.Popen('dir %s' % self.userhome_dir, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout = cmd_boj.stdout.read()
stderr = cmd_boj.stderr.read()
cmd_result = (stdout + stderr).decode('GBK')
if not cmd_result:
self.send_response(status_code=101)
else:
self.send_response(status_code=0, dir_info=cmd_result) def _cd(self, data):
'''进入用户想要进入的目录当中'''
dirname = data['dirname']
if not dirname.startswith('.'):
if os.path.isdir(os.path.join(self.userhome_dir, dirname)):
self.userhome_dir = os.path.join(self.userhome_dir, dirname)
self.client_dir = self.userhome_dir.replace(settings.USER_HOME_DIR, '')
self.send_response(status_code=401, dirn=self.client_dir)
else:
self.send_response(status_code=400)
else:
li = dirname.split('\\')
count = 0
while count < len(li):
'''循环 如果 当前路径 已经到用户的家目录,就返回500 已经到达最顶层'''
if self.userhome_dir == self.home_dir:
self.send_response(status_code=500)
else:
self.userhome_dir = os.path.dirname(self.userhome_dir)
self.client_dir = self.userhome_dir.replace(settings.USER_HOME_DIR, '')
self.send_response(status_code=0, dirn=self.client_dir)
count += 1 def _make_dir(self):
'''创建用户想要创建的文件夹'''
print('创建用户想要创建的文件夹')
main.py
FTPserver的更多相关文章
- 使用apache ftpserver搭建ftp服务器
作为一个javaer,遇到任何问题,先查一下java中的解决方案.地球上的许多事情,在java中都能找到完美的解决方案.之前搭建ftp服务器使用的是vsftpd,现在可以把它卸掉了,它以服务的形式运行 ...
- Apache FtpServer扩展【动手实现自己的业务】
Apache FtpServer是当下最热门的走ftp协议的用于用户上传下载的服务器. 官网http://mina.apache.org/ftpserver-project/ 一般来说,用的话,去 ...
- 02.Apache FtpServer使用数据库管理用户
1.创建数据库及表 使用\apache-ftpserver-1.0.6\res\ftp-db.sql建表,内容如下: CREATE TABLE FTP_USER ( userid VARCHAR(64 ...
- 01.Apache FtpServer配置
1.解压Apache FTPServer 将下载下来的压缩包(ftpserver-1.0.6.zip)解压到本地,其目录结构如下图: 2.修改users.properties 修改 \apache-f ...
- apache FtpServer 整合spring部署
我们在项目中可能会出现这样的需求,使用ftp上传很大的文件后对需要对文件进行相应的逻辑处理,这时我们可以使用apache ftpServer来处理这段逻辑,只要我们做相应的部署和编写我们的逻辑代码,这 ...
- apache FtpServer整合spring逻辑处理
上面我们的部署工作完成了,那么文件上传下载后,ftpserver会自动相应我们的上传下载操作,也就是说ftpServer服务器会得到触发,那么我们如果要得到文件的一些信息,比如说文件的路径.大小.类型 ...
- ubuntu12.04下搭建ftpserver
楼主想把同学硬盘里面的200多G电影共享给实验室的小伙伴们看,就打算在内网server上搭建一个ftp的server. 1.安装ftp 首先在终端下键入例如以下内容,安装ftpserver: sudo ...
- 在内网架设一个可供外网登录的ftpserver
ftpserver是使用比較寻常的server,可是IP资源是有限的.那么怎么让内网的server給外网的用户提供服务了? 首先须要找一个FTPserver程序,我在这边使用pure-ftpd-mys ...
- Windows在结构FTPserver
同Windows8 案件,结构介绍 FTPserver脚步: 1.为Windows开启FTP功能:控制面板->程序->启用或关闭Windows功能.将下图所看到的的复选框选中 waterm ...
- Apache FtpServer 实现文件的上传和下载
1 下载需要的jar包 Ftp服务器实现文件的上传和下载,主要依赖jar包为: 2 搭建ftp服务器 参考Windows 上搭建Apache FtpServer,搭建ftp服务器 3 主要代码 在ec ...
随机推荐
- unity 4.6.1脚本解析出错,没有激活的勾,方法顺序出错
检查方法声明上的注释:如/**xx*/或/*xx*/改为//形式 没有激活的勾: 1.如/**xx*/或/*xx*/改为//形式 2.必须保留Start函数
- ReactiveX 学习笔记(9)工具类操作符
Observable Utility Operators 本文的主题为处理 Observable 的实用工具类操作符. 这里的 Observable 实质上是可观察的数据流. RxJava操作符(六) ...
- Haskell语言学习笔记(84)Concurrent
Control.Concurrent Prelude> import Control.Concurrent Prelude Control.Concurrent> Control.Conc ...
- 【Maven】项目打包-war包-Jar包[IDEA将项目打成war包]
[Maven]项目打包-war包-Jar包[IDEA将项目打成war包] 2017年01月31日 00:21:06 阅读数:22912 标签: ideamaven发布博客插件 更多 个人分类: ❷ J ...
- C# 如何获取鼠标在屏幕上的位置,不论程序是否为活动状态
一开始我认为应该使用HOOK来写,而且必须使用全局HOOK,结果在一次偶然的机会得到,原来其实根本没有那个必要. 直接上代码吧,一看就明白 Point ms = Control.MousePositi ...
- spring boot 程序打jar包及运行
- 吴裕雄 28-MySQL 序列使用
MySQL序列是一组整数:1, 2, 3, ...,由于一张数据表只能有一个字段自增主键, 如果你想实现其他字段也实现自动增加,就可以使用MySQL序列来实现 使用AUTO_INCREMENTMySQ ...
- python3进行汉字和unicode码的转换
输出某个unicode码对应的汉字和某个汉字对应的unicode编码. # -*- coding=UTF-8 -*- str1 = "\u6000"#某个汉字的unicode码 s ...
- Redis安装完后redis-cli无法使用(redis-cli: command not found)已使用
wget http://download.redis.io/redis-stable.tar.gz(下载redis-cli的压缩包) tar xvzf redis-stable.tar.gz(解压) ...
- 新手如何学习Java——Java学习路线图
推荐初学者阅读:新手如何学习Java——Java学习路线图