django+paramiko结合layui实现webssh,sftp的多文件直传功能

文章描述了如何使用Django框架、Paramiko库和layui组件结合,实现Web终端的SFTP多文件上传功能,包括views.py中的视图处理逻辑和前端文件选择及上传过程。

原文地址:django+paramiko结合layui实现webssh,sftp的多文件直传功能

环境准备

python: 3.7.5

django:3.2.15

paramiko:3.1.0

layui:2.8.2

要求django+layui基础环境已经搭建完成。

主方法--views.py

# 上传基于SFTP直传--web终端
@csrf_exempt
@xframe_options_exempt
@k8s.login_auth_required
def webssh_upload_terminal_info(request):
    id = request.GET.get("id")
    remote_path = request.GET.get('selectedFilePath')
    print("文件上传--web终端主机ID:",id)
    print("传递的路径地址:",remote_path)
    return render(request, 'monitor/webssh_upload_terminal.html',{"id":id,"remote_path":remote_path})

@csrf_exempt
@xframe_options_exempt
@k8s.login_auth_required
def webssh_update_file_api(request):
    if request.method == "POST":
        host_ssh_id = request.GET.get('id')
        remote_path = request.POST.get('remote_path')
        file = request.FILES.get('file')
        print("主机ID:",host_ssh_id,"上传路径:",remote_path,"文件:",file)

        # 允许上传的文件格式
        allowed_types = [
            'application/octet-stream',
            'application/zip',
            'application/x-zip-compressed',
            'image/jpeg',
            'image/png',
            'image/gif',
            'image/x-icon',
        ]

        # 如果想不限制文件的上传类型,可以去掉这个判断即可
        if file.content_type in allowed_types:
            # 文件在内存中,将其内容写入临时文件
            with tempfile.NamedTemporaryFile(delete=False) as temp_file:
                temp_file.write(file.read())
                file_path = temp_file.name
            
            print("文件临时路径:",file_path)
            try:
                # 通过ID在数据库中查出用于认证的信息并建立连接
                host_ssh = HostMonitoring.objects.get(id=host_ssh_id)
                host_ip = host_ssh.ipv4_address
                host_port = int(host_ssh.port)
                sys_user_name = host_ssh.username
                sys_user_passwd = host_ssh.password
                try:
                    # 建立SSH连接
                    with paramiko.Transport((host_ip, host_port)) as sftp_client:
                        sftp_client.connect(username=sys_user_name, password=sys_user_passwd)
                        sftp = paramiko.SFTPClient.from_transport(sftp_client)
                        sftp.put(file_path, remote_path + '/' + file.name)
                        sftp_client.close()
                    return JsonResponse({'success': True, 'message': '文件上传成功'})
                except HostMonitoring.DoesNotExist:
                    return JsonResponse({'success': False, 'message': '无效的主机ID'})
                except Exception as e:
                    return JsonResponse({'success': False, 'message': str(e)})
            except Exception as e:
                return JsonResponse({'success': False, 'message': '数据库连接异常'})
        else:
            print("上传失败",file.content_type)
            return JsonResponse({'success': False, 'message': '无效的文件类型'})
    else:
        return JsonResponse({'success': False, 'message': '请求方法错误'})

前端文件--webssh_upload_terminal.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>文件上传</title>
  <link href="/static/layui/css/layui.css" rel="stylesheet" type="text/css"/>
  <style>
    .upload-status {
      font-weight: bold;
    }
    .upload-success {
      color: green;
    }
    .upload-failed {
      color: red;
    }
  </style>
</head>
<body>
    <div class="layui-upload">
      <div class="layui-inline">
        <button type="button" class="layui-btn layui-btn-normal" id="ID-upload-demo-files">选择多文件</button>
      </div>
      <!-- <div class="layui-inline">
        <label class="layui-form-label">远程路径:</label>
        <div class="layui-input-inline">
            <input type="text" id="remote-path" placeholder="请输入远程存储路径" autocomplete="off" class="layui-input">
        </div>
      </div> -->
      <div class="layui-inline">
        <button type="button" class="layui-btn layui-btn-warm" id="ID-upload-demo-files-action">开始上传</button> 
    </div>
        <div class="layui-upload-list">
          <table class="layui-table">
            <colgroup>
              <col style="min-width: 100px;">
              <col width="150">
              <col width="260">
              <col width="150">
            </colgroup>
            <thead>
              <th>文件名</th>
              <th>大小</th>
              <th>上传进度</th>
              <th>操作</th>
            </thead>
            <tbody id="ID-upload-demo-files-list"></tbody>
          </table>
        </div>
      </div>

<script src="/static/layui/layui.js"></script>
<script>
  layui.use(['jquery','upload','element'],function(upload){
    var upload = layui.upload;
    var element = layui.element;
    var $ = layui.$;
    var hostId = "{{ id }}";
    var remote_path = "{{ remote_path }}";
    console.log("第二层弹出上传栏:" + remote_path)
    var uploadListIns = upload.render({
      elem: '#ID-upload-demo-files',
      elemList: $('#ID-upload-demo-files-list'),
      url: '{% url "webssh_update_file_api" %}?id=' + hostId,
      accept: 'file',
      multiple: true,
      number: 10,
      auto: false,
      data:{remote_path: ''},
      bindAction: '#ID-upload-demo-files-action',
      before: function(obj){
        this.data.remote_path = remote_path
      },
      choose: function(obj){  
        var that = this;
        var files = this.files = obj.pushFile();
        
        obj.preview(function(index, file, result){
          var tr = $(['<tr id="upload-'+ index +'">',
            '<td>'+ file.name +'</td>',
            '<td>'+ (file.size/1024/1024).toFixed(1) +'Mb</td>',
            '<td><div class="layui-progress" lay-filter="progress-demo-'+ index +'"><div class="layui-progress-bar" lay-percent=""></div></div></td>',
            '<td><button class="layui-btn layui-btn-xs demo-delete">删除</button></td>',
            '<td id="status-'+ index +'" class="upload-status">等待上传</td>',
          '</tr>'].join(''));
          
          tr.find('.demo-delete').on('click', function(){
            delete files[index];
            tr.remove();
            uploadListIns.config.elem.next()[0].value = ''; 
          });
          
          that.elemList.append(tr);
          element.render('progress');
        });
      },
      done: function(res, index, upload){
        var statusElem = $('#status-' + index);
        if (res.success) {
          statusElem.html('<span class="upload-success">上传成功</span>');
        } else {
          statusElem.html('<button class="layui-btn layui-btn-xs demo-reload">重传</button>' +
                          '<button class="layui-btn layui-btn-xs layui-btn-danger demo-delete">删除</button>');
        }
      },
      error: function(index, upload){
        var statusElem = $('#status-' + index);
        statusElem.html('<button class="layui-btn layui-btn-xs demo-reload">重传</button>' +
                        '<button class="layui-btn layui-btn-xs layui-btn-danger demo-delete">删除</button>');
      },
      progress: function(n, elem, e, index){
        var statusElem = $('#status-' + index);
        statusElem.text('上传中 ' + n + '%');
        element.progress('progress-demo-'+ index, n + '%');
      }
    });
    
    $('#ID-upload-demo-files-action').on('click', function(){
      // Change the status to "上传中" for all selected files
      $('.upload-status').text('上传中 0%').addClass('upload-failed').removeClass('upload-success');
      $('.layui-progress-bar').css('width', '0%');
    });
  });
</script>
</body>
</html>

前端文件--webssh_file.html

<!--文件sftp直传代码-->
  <script>
    layui.use('layer', function () {
      var layer = layui.layer;
      var hostId = "{{ id }}";

      $('#upload').on('click', function () {
        var selectedFilePath = $("#current-path").val();
        console.log("获取到的路径" + selectedFilePath)
        layer.open({
          type: 2,
          offset: 'r',
          anim: 'slideLeft', // 从右往左
          area: ['70%', '100%'],
          closeBtn: true, // 1或者2表示开启关闭按钮,0表示不开启
          title: "文件管理器",
          shade: 0.1,
          shift: 2,
          shadeClose: false,
          id: 'ID-demo-layer-direction-r', //2.8.0新特性,抽屉效果
          content: '{% url "webssh_upload_terminal_info" %}?id=' + hostId + '&selectedFilePath=' + selectedFilePath,
          move: false, // 禁止拖动
          resize: false, // 禁止调整大小
          skin: 'white-background' // 应用自定义的背景颜色类 
        });
      });
    });
  </script>
  <!--上传进度条弹窗-->
  <div id="file-upload-progress" style="margin:50px 0 0 50px;display: none;">
    <div id="picker" style="float:left;">请选择</div>
    <div id="progress" class="progress" style="width:500px;float:left;margin:10px 0 0 20px;">
      <div class="progress-bar progress-bar-striped active" role="progressbar" style="width:0%;"></div>
    </div>
    <div style="clear:both;"></div>
  </div>

  <!--头部选项栏-->
  <div class="layui-container">
    <div class="layui-row layui-col-space15">
      <div class="layui-col-md12">
        <fieldset class="layui-elem-field layui-field-title">
          <legend>Linux 文件管理器</legend>
          <div class="layui-field-box">
            <div class="layui-form-item">
              <button id="go-back" class="layui-btn return-btn control-btn">返回</button>
              <label class="layui-form-label" style="margin-left: 20px;">当前路径:</label>
              <div class="layui-input-inline">
                <input type="text" id="current-path" value="/" readonly class="layui-input">
              </div>
              <button class="layui-btn control-btn action-btn" data-action="createDir">新建目录</button>
              <!-- 设置一个隐藏的file input接收用户上传的文件-->
              <input type="file" id="file-upload" style="display: none;">
              <!--将上传文件按钮设为触发file input的点击事件-->
              <button class="layui-btn layui-btn-normal control-btn upload-file" id="upload" data-action="upload">上传文件</button>  <!--文件sftp直传方式入口-->
              <!-- 实现弹出层 -->
              <button id="test1">查看上传进度</button> <!-- 触发按钮 -->
              <button id="refresh" class="layui-btn layui-btn-normal control-btn">刷新</button>
            </div>
            <table id="file-table" lay-filter="demo"></table>
            <script type="text/html" id="operations">
              <button class="layui-btn layui-btn-xs layui-btn-normal download" data-action="download">下载</button>
              <button class="layui-btn layui-btn-xs layui-btn-danger delete" data-action="delete">删除</button>
          </script>
          </div>
        </fieldset>
      </div>
    </div>
  </div>

效果图如下

sftp_upload.gif

这段代码的流程如下:linux 文件管理器选择要上传的文件目录》选择上传》选择文件》开始上传 如果上传失败,会显示重传按钮,功能还没实现,上传文件会先保存到/tmp临时目录下,上传浏览器端上传主机完成,会写入到用户选择的上传目录下。

这个版本体验要好一点了,且支持多文件同时上传,但仍需要打磨。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

llody_55

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值