Unverified Commit 7e654cd6 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #256 from jumpserver/dev

支持上传目录和删除目录 (#250)
parents 6b5e9dd1 1919898d
......@@ -376,6 +376,7 @@ defaults = {
'SFTP_SHOW_HIDDEN_FILE': False,
'UPLOAD_FAILED_REPLAY_ON_START': True,
'REUSE_CONNECTION': True,
'FORCE_REMOVE_FOLDER': False,
}
......
......@@ -31,7 +31,7 @@ class ElFinderConnector:
_allowed_args = [
'cmd', 'target', 'targets[]', 'current', 'tree',
'name', 'content', 'src', 'dst', 'cut', 'init',
'type', 'width', 'height', 'upload[]', 'dirs[]',
'type', 'width', 'height', 'upload[]', 'upload_path[]', 'dirs[]',
'targets', "chunk", "range", "cid", 'reload',
]
......@@ -125,7 +125,7 @@ class ElFinderConnector:
# Copy allowed parameters from the given request's GET to self.data
for field in self._allowed_args:
if field in request_data:
if field in ["targets[]", "targets", "dirs[]"]:
if field in ["targets[]", "targets", "dirs[]", 'upload_path[]']:
self.data[field] = request_data.getlist(field)
else:
self.data[field] = request_data[field]
......@@ -270,20 +270,21 @@ class ElFinderConnector:
parent = self.data['target']
volume = self.get_volume(parent)
upload = self.data.get('upload[]')
upload_paths = self.data.get('upload_path[]')
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
self.request.files, self.data.get('chunk'), parent, upload_paths
)
)
elif self.data.get('chunk'):
self.response.update(
volume.upload_chunk_merge(parent, self.data.get('chunk'))
volume.upload_chunk_merge(parent, self.data.get('chunk'), upload_paths),
)
elif isinstance(upload, str):
self.response.update(volume.upload_as_url(upload, parent))
self.response.update(volume.upload_as_url(upload, parent, upload))
else:
self.response.update(volume.upload(self.request.files, parent))
self.response.update(volume.upload(self.request.files, parent, upload_paths))
def __size(self):
target = self.data['targets[]']
......
......@@ -214,23 +214,25 @@ class BaseVolume:
"""
raise NotImplementedError
def upload(self, files, parent):
def upload(self, files, parent, upload_path):
""" Uploads one or more files in to the parent directory.
:param files: A list of uploaded file objects, as described here:
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/
:param parent: The hash of the directory in which to create the
new files.
:param upload_path:
:returns: TODO
"""
raise NotImplementedError
def upload_as_chunk(self, files, chunk_name, parent):
def upload_as_chunk(self, files, chunk_name, parent, upload_path):
"""
Upload a large file as chunk
:param files:
:param chunk_name:
:param cid:
:param parent:
:param upload_path:
:return:
"""
......@@ -4,7 +4,7 @@ import stat
import threading
from flask import send_file
import requests
import os
from coco.utils import get_logger
from .base import BaseVolume
......@@ -222,10 +222,10 @@ class SFTPVolume(BaseVolume):
self.sftp.unlink(remote_path)
return target
def upload_as_url(self, url, parent):
def upload_as_url(self, url, parent, upload_path):
raise PermissionError("Not support upload from url")
def upload(self, files, parent):
def upload(self, files, parent, upload_path):
""" For now, this uses a very naive way of storing files - the entire
file is read in to the File model's content field in one go.
......@@ -234,10 +234,13 @@ class SFTPVolume(BaseVolume):
"""
added = []
parent_path = self._path(parent)
item = files.get('upload[]')
for i, item in enumerate(files.getlist("upload[]")):
if upload_path and (parent != upload_path[i]) and (item.filename in upload_path[i]):
path = self._join(parent_path, upload_path[i].lstrip(self.path_sep))
else:
path = self._join(parent_path, item.filename)
remote_path = self._remote_path(path)
infos = self._list(parent_path)
infos = self._list(os.path.dirname(path))
files_exist = [d['name'] for d in infos]
if item.filename in files_exist:
raise OSError("File {} exits".format(remote_path))
......@@ -247,7 +250,7 @@ class SFTPVolume(BaseVolume):
added.append(self._info(path))
return {'added': added}
def upload_as_chunk(self, files, chunk_name, parent):
def upload_as_chunk(self, files, chunk_name, parent, upload_path):
added = []
parent_path = self._path(parent)
item = files.get('upload[]')
......@@ -255,11 +258,13 @@ class SFTPVolume(BaseVolume):
filename = '.'.join(__tmp[:-2])
num, total = __tmp[-2].split('_')
num, total = int(num), int(total)
path = self._join(parent_path, filename)
if len(upload_path) == 1 and (parent != upload_path[0]) and (filename in upload_path[0]):
path = self._join(parent_path, upload_path[0].lstrip(self.path_sep))
else:
path = self._join(parent_path, upload_path[0].lstrip(self.path_sep), filename)
remote_path = self._remote_path(path)
if num == 0:
infos = self._list(parent_path)
infos = self._list(os.path.dirname(path))
files_exist = [d['name'] for d in infos]
if item.filename in files_exist:
raise OSError("File {} exits".format(remote_path))
......@@ -271,8 +276,11 @@ class SFTPVolume(BaseVolume):
else:
return {'added': added, '_chunkmerged': filename, '_name': filename}
def upload_chunk_merge(self, parent, chunk):
def upload_chunk_merge(self, parent, chunk, upload_path):
parent_path = self._path(parent)
if len(upload_path) == 1 and (parent != upload_path[0]):
path = self._join(parent_path, upload_path[0].lstrip(self.path_sep))
else:
path = self._join(parent_path, chunk)
return {"added": [self._info(path)]}
......
......@@ -422,7 +422,7 @@
'minsLeft' : '剩余 $1 分钟', // from v2.1.17 added 13.11.2016
'openAsEncoding' : '使用所选编码重新打开', // from v2.1.19 added 2.12.2016
'saveAsEncoding' : '使用所选编码保存', // from v2.1.19 added 2.12.2016
'selectFolder' : '选择目录(暂不支持)', // from v2.1.20 added 13.12.2016
'selectFolder' : '选择目录', // from v2.1.20 added 13.12.2016
'firstLetterSearch': '首字母搜索', // from v2.1.23 added 24.3.2017
'presets' : '预置', // from v2.1.25 added 26.5.2017
'tooManyToTrash' : '项目太多,不能移动到回收站.', // from v2.1.25 added 9.6.2017
......
import os
import stat
import paramiko
import time
from datetime import datetime
......@@ -47,6 +48,7 @@ def convert_error(func):
if isinstance(error, Exception):
logger.error(error)
return response
return wrapper
......@@ -74,6 +76,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
self._sftp = {}
self.hosts = self.get_permed_hosts()
self.is_finished = False
self.force_rm_folder = config.FORCE_REMOVE_FOLDER
def get_user_assets(self):
user_id = self.server.connection.user.id
......@@ -392,12 +395,24 @@ class SFTPServer(paramiko.SFTPServerInterface):
success = False
try:
if self.force_rm_folder:
self._rmdir(client, rpath)
else:
client.rmdir(rpath)
success = True
return paramiko.SFTP_OK
finally:
self.create_ftp_log(path, "Rmdir", success)
def _rmdir(self, sftp_client, path):
for item in list(sftp_client.listdir_iter(path)):
filepath = "/".join([path, item.filename])
if stat.S_IFMT(item.st_mode) == stat.S_IFDIR:
self._rmdir(sftp_client, filepath)
continue
sftp_client.remove(filepath)
sftp_client.rmdir(path)
class FakeServer:
pass
......
......@@ -60,3 +60,6 @@ BOOTSTRAP_TOKEN: <PleasgeChangeSameWithJumpserver>
# 是否复用和用户后端资产已建立的连接(用户不会复用其他用户的连接)
# REUSE_CONNECTION: true
# 是否强制删除文件夹:(default false)
# FORCE_REMOVE_FOLDER: false
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