Commit 90ce603e authored by ibuler's avatar ibuler

[Update] 支持上传大文件

parent 5823d999
#!/usr/bin/python
#
from coco.httpd import app
from coco.logger import create_logger
create_logger()
if __name__ == '__main__':
app.run()
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
import eventlet import eventlet
from eventlet.debug import hub_prevent_multiple_readers from eventlet.debug import hub_prevent_multiple_readers
eventlet.monkey_patch() # eventlet.monkey_patch()
hub_prevent_multiple_readers(False) # hub_prevent_multiple_readers(False)
import datetime import datetime
import os import os
...@@ -19,8 +19,7 @@ from .sshd import SSHServer ...@@ -19,8 +19,7 @@ from .sshd import SSHServer
from .httpd import HttpServer from .httpd import HttpServer
from .logger import create_logger from .logger import create_logger
from .tasks import TaskHandler from .tasks import TaskHandler
from .utils import get_logger, ugettext as _, \ from .utils import get_logger, ugettext as _, ignore_error
ignore_error
from .ctx import app_service from .ctx import app_service
from .recorder import get_replay_recorder from .recorder import get_replay_recorder
from .session import Session from .session import Session
......
...@@ -14,6 +14,7 @@ app = Flask(__name__, template_folder='templates', static_folder='static') ...@@ -14,6 +14,7 @@ app = Flask(__name__, template_folder='templates', static_folder='static')
app.config.update(config) app.config.update(config)
socket_io = SocketIO() socket_io = SocketIO()
socket_io.on_namespace(ProxyNamespace('/ssh')) socket_io.on_namespace(ProxyNamespace('/ssh'))
socket_io.on_namespace(ProxyNamespace('/coco/ssh'))
# init_kwargs = {'async_mode': 'threading'} # init_kwargs = {'async_mode': 'threading'}
init_kwargs = {'async_mode': 'eventlet'} init_kwargs = {'async_mode': 'eventlet'}
......
...@@ -33,12 +33,12 @@ class ElFinderConnector: ...@@ -33,12 +33,12 @@ class ElFinderConnector:
'cmd', 'target', 'targets[]', 'current', 'tree', 'cmd', 'target', 'targets[]', 'current', 'tree',
'name', 'content', 'src', 'dst', 'cut', 'init', 'name', 'content', 'src', 'dst', 'cut', 'init',
'type', 'width', 'height', 'upload[]', 'dirs[]', 'type', 'width', 'height', 'upload[]', 'dirs[]',
'targets' 'targets', "chunk", "range", "cid",
] ]
_options = { _options = {
'api': _version, 'api': _version,
'uplMaxSize': '128M', 'uplMaxSize': '10M',
'options': { 'options': {
'separator': '/', 'separator': '/',
'disabled': [], 'disabled': [],
...@@ -113,13 +113,6 @@ class ElFinderConnector: ...@@ -113,13 +113,6 @@ class ElFinderConnector:
except Exception as e: except Exception as e:
self.response['error'] = '%s' % e self.response['error'] = '%s' % e
logger.exception(e) logger.exception(e)
finally:
target = self.data['target']
if not target:
return
volume = self.get_volume(target)
if volume:
volume.close()
def get_request_data(self): def get_request_data(self):
data_source = {} data_source = {}
...@@ -282,7 +275,19 @@ class ElFinderConnector: ...@@ -282,7 +275,19 @@ class ElFinderConnector:
def __upload(self): def __upload(self):
parent = self.data['target'] parent = self.data['target']
volume = self.get_volume(parent) volume = self.get_volume(parent)
self.response.update(volume.upload(self.request.files, parent)) if self.data.get('chunk') and self.data.get('cid'):
self.response.update(
volume.upload_as_chunk(
self.request.files, self.data.get('chunk'), parent
)
)
elif self.data.get('chunk'):
self.response.update(
volume.upload_chunk_merge(parent, self.data.get('chunk'))
)
else:
print("__UPLOAD {}".format(self.data))
self.response.update(volume.upload(self.request.files, parent))
def __size(self): def __size(self):
target = self.data['target'] target = self.data['target']
......
...@@ -223,3 +223,13 @@ class BaseVolume: ...@@ -223,3 +223,13 @@ class BaseVolume:
:returns: TODO :returns: TODO
""" """
raise NotImplementedError raise NotImplementedError
def upload_as_chunk(self, files, chunk_name, parent):
"""
Upload a large file as chunk
:param files:
:param chunk_name:
:param cid:
:param parent:
:return:
"""
import logging import logging
import stat import stat
import re
from flask import send_file from flask import send_file
...@@ -198,6 +199,36 @@ class SFTPVolume(BaseVolume): ...@@ -198,6 +199,36 @@ class SFTPVolume(BaseVolume):
added.append(self._info(path)) added.append(self._info(path))
return {'added': added} return {'added': added}
def upload_as_chunk(self, files, chunk_name, parent):
added = []
print("Upload chunk: {}".format(chunk_name))
parent_path = self._path(parent)
item = files.get('upload[]')
__tmp = chunk_name.split('.')
filename = '.'.join(__tmp[:-2])
num, total = __tmp[-2].split('_')
num, total = int(num), int(total)
path = self._join(parent_path, filename)
remote_path = self._remote_path(path)
if num == 0:
infos = self._list(parent_path)
files_exist = [d['name'] for d in infos]
if item.filename in files_exist:
raise OSError("File {} exits".format(remote_path))
with self.sftp.open(remote_path, 'a') as rf:
for data in item:
rf.write(data)
if num != total:
return {'added': added}
else:
return {'added': added, '_chunkmerged': filename, '_name': filename}
def upload_chunk_merge(self, parent, chunk):
parent_path = self._path(parent)
path = self._join(parent_path, chunk)
return {"added": [self._info(path)]}
def size(self, target): def size(self, target):
info = self.info(target) info = self.info(target)
return info.get('size') or 'Unknown' return info.get('size') or 'Unknown'
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
] ]
}, },
height: $(window).height(), height: $(window).height(),
url: '{{ url_for("sftp_connector_view") }}', url: '{{ url_for("sftp_host_connector_view", host=host) }}',
resizable: false, resizable: false,
lang: 'pl', lang: 'pl',
requestType: 'get', requestType: 'get',
...@@ -39,10 +39,7 @@ ...@@ -39,10 +39,7 @@
}, },
rememberLastDir: false, rememberLastDir: false,
placesFirst: false, placesFirst: false,
reloadClearHistory: true, reloadClearHistory: true
handlers: {
'open': function(event) { console.log(event.data); }
}
}; };
var start = function(lng) { var start = function(lng) {
$(function() { $(function() {
......
<html>
<head>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-2.1.1.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-ui-1.10.4.min.js') }}"></script>
<link rel="stylesheet" type="text/css" media="screen" href="{{ url_for('static', filename='elfinder/css/elfinder.min.css') }}">
<link rel="stylesheet" type="text/css" media="screen" href="{{ url_for('static', filename='elfinder/css/theme-gray.css') }}">
<script type="text/javascript" src="{{ url_for('static', filename='elfinder/elfinder.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='elfinder/i18n/elfinder.pl.js') }}"></script>
</head>
<body style="margin: 0">
<div id="elfinder"></div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
var elf;
var opts = {
uiOptions : {
toolbar: [
['back', 'forward'],
['mkdir', 'mkfile'],
['copy', 'cut', 'paste'],
['rm'],
['rename'],
['view']
]
},
// ui: [],
height: $(window).height(),
url: '{{ url_for("sftp_host_connector_view", host=host) }}',
resizable: false,
lang: 'pl',
rememberLastDir: false,
reloadClearHistory: true,
contextmenu: {
navbar: [
'rm'
],
cwd: [
'reload', 'back', '|', 'mkdir', 'mkfile', '|',
'upload'
],
files: [
'rm', 'rename', 'download'
]
},
placesFirst: false
};
var start = function(lng) {
$(function() {
// Make elFinder (REQUIRED)
opts.lang = lng;
elf = $('#elfinder').elfinder(opts).elfinder('instance');
});
};
var load = function () {
var loct = window.location.search;
var full_lng, locm, lng;
if (loct && (locm = loct.match(/lang=([a-zA-Z_-]+)/))) {
full_lng = locm[1];
} else {
full_lng = (navigator.browserLanguage || navigator.language || navigator.userLanguage);
}
lng = full_lng.substr(0,2);
if (lng === 'ja') {
lng = 'jp';
}
else if (lng === 'pt') {
lng = 'pt_BR';
}
else if (lng === 'zh') {
lng = (full_lng.substr(0,5) == 'zh-tw')? 'zh_TW' : 'zh_CN';
}
if (lng !== 'en') {
$.ajax({
url : "{{ url_for('static', filename='elfinder/i18n/') }}"+'/elfinder.'+lng+'.js',
cache : true,
dataType : 'script'
})
.done(function() {
start(lng);
})
.fail(function() {
start('en');
});
} else {
start(lng);
}
};
load();
var resizeTimer;
$(window).resize(function () {
resizeTimer && clearTimeout(resizeTimer);
if (!$('#elfinder').hasClass('elfinder-fullscreen')) {
resizeTimer = setTimeout(function () {
var h = parseInt($(window).height());
if (h != parseInt($('#elfinder').height())) {
elf.resize('100%', h);
}
}, 200);
}
});
});
</script>
</body>
</html>
...@@ -4,14 +4,14 @@ ...@@ -4,14 +4,14 @@
from flask import render_template, request, jsonify from flask import render_template, request, jsonify
from .app import app from .app import app
from .finder import connector, volumes from .elfinder import connector, volumes
from ..models import Connection from ..models import Connection
from ..sftp import InternalSFTPClient from ..sftp import InternalSFTPClient
from .auth import login_required from .auth import login_required
from ..service import app_service from ..service import app_service
@app.route('/elfinder/sftp/connector/<host>/', methods=['GET', 'POST']) @app.route('/coco/elfinder/sftp/connector/<host>/', methods=['GET', 'POST'])
@login_required @login_required
def sftp_host_connector_view(host): def sftp_host_connector_view(host):
user = request.current_user user = request.current_user
...@@ -19,7 +19,7 @@ def sftp_host_connector_view(host): ...@@ -19,7 +19,7 @@ def sftp_host_connector_view(host):
connection.user = user connection.user = user
sftp = InternalSFTPClient(connection) sftp = InternalSFTPClient(connection)
volume = volumes.SFTPVolume(sftp) volume = volumes.SFTPVolume(sftp)
if host: if host != '_':
asset = app_service.get_asset(host) asset = app_service.get_asset(host)
if not asset: if not asset:
return jsonify({'error': 'Not found this host'}) return jsonify({'error': 'Not found this host'})
...@@ -31,28 +31,20 @@ def sftp_host_connector_view(host): ...@@ -31,28 +31,20 @@ def sftp_host_connector_view(host):
handler = connector.ElFinderConnector([volume]) handler = connector.ElFinderConnector([volume])
handler.run(request) handler.run(request)
# Some commands (e.g. read file) will return a Django View - if it # If download file, return a view response
# is set, return it directly instead of building a response
if handler.return_view: if handler.return_view:
return handler.return_view return handler.return_view
if handler.headers['Content-type'] == 'application/json': if handler.headers['Content-type'] == 'application/json':
return jsonify(handler.response) return jsonify(handler.response)
@app.route('/elfinder/sftp/connector/', methods=['GET', 'POST']) @app.route('/coco/elfinder/sftp/<host>/')
@login_required
def sftp_connector_view():
return sftp_host_connector_view('')
@app.route('/elfinder/sftp/<host>/')
def sftp_host_finder(host): def sftp_host_finder(host):
return render_template('finder/host_file_manager.html', host=host) return render_template('finder/file_manager.html', host=host)
@app.route('/elfinder/sftp/') @app.route('/coco/elfinder/sftp/')
def sftp_finder(): def sftp_finder():
return render_template('finder/file_manager.html') return render_template('finder/file_manager.html', host='_')
...@@ -27,11 +27,13 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -27,11 +27,13 @@ class SFTPServer(paramiko.SFTPServerInterface):
trans = chan.get_transport() trans = chan.get_transport()
sftp.close() sftp.close()
if [c for c in trans._channels.values() if not c.closed]: active_channels = [c for c in trans._channels.values() if not c.closed]
if not active_channels:
print("CLose transport")
trans.close() trans.close()
if sock: if sock:
sock.close() sock.close()
sock.transport.close() sock.transport.close()
self._sftp = {} self._sftp = {}
def get_host_sftp(self, host, su): def get_host_sftp(self, host, su):
...@@ -100,7 +102,6 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -100,7 +102,6 @@ class SFTPServer(paramiko.SFTPServerInterface):
return False return False
def create_ftp_log(self, path, operate, is_success=True, filename=None): def create_ftp_log(self, path, operate, is_success=True, filename=None):
print("Create ftp log: {} {} {}".format(path, operate, filename))
host, su, rpath = self.parse_path(path) host, su, rpath = self.parse_path(path)
asset = self.hosts.get(host) asset = self.hosts.get(host)
date_start = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") + " +0000", date_start = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") + " +0000",
...@@ -361,6 +362,7 @@ class InternalSFTPClient(SFTPServer): ...@@ -361,6 +362,7 @@ class InternalSFTPClient(SFTPServer):
except OSError: except OSError:
raise OSError("Open file {} failed".format(rpath)) raise OSError("Open file {} failed".format(rpath))
finally: finally:
print("Open success")
self.create_ftp_log(path, operate, success) self.create_ftp_log(path, operate, success)
return f return f
...@@ -371,4 +373,5 @@ class InternalSFTPClient(SFTPServer): ...@@ -371,4 +373,5 @@ class InternalSFTPClient(SFTPServer):
return self.remove(path) return self.remove(path)
def close(self): def close(self):
print("End ")
return self.session_ended() return self.session_ended()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment