使用 Paramiko put 通过 POST 请求上传文件

Upload file via POST request using Paramiko put

我正在尝试使用 POST 请求发送文件,然后使用 paramiko 将其转发到服务器。我有以下代码:

@app.route("/", methods=['GET', 'POST'])
def upload():

   file = request.files['file']
   try:
       gethostbyname("servername")
   except gaierror:
       print(gaierror)
       exit()
   port = 22

   if request.method == 'POST':
       filePost = request.args.get('filename')

       transport = paramiko.Transport("servername", port))
       try:
           transport.connect(username="user",password="password", hostkey=None)
           sftp = paramiko.SFTPClient.from_transport(transport)
       except paramiko.ssh_exception.AuthenticationException as e:
           print(e)
           exit()
       path = '/home/'+file.filename
       try:
           sftp.put(file, path)
       except Exception as e:
           print(e)

很遗憾,没有上传任何内容。恐怕我必须先保存来自 POST 请求的文件。有可能解决这个问题吗?还是我遗漏了什么?

来自我的 post 请求的 key/value(使用 Postman):
key:file
value:test.txt

您的代码将受益于一些重组和清理,实际上看起来您删除了一些行。

主要问题是您正在尝试通过 SFTP 从 POST 参数中获取 file,而 sftp.put 期望来自 FS 的文件路径作为第一个参数。

这里有一个 re-organized 函数,对我有用。您可以注意到该文件首先保存到 FS (file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))),然后将相同的字符串传递给 sftp.put:

import os
import sys
import paramiko
from werkzeug.utils import secure_filename
from flask import Flask, render_template, redirect, url_for, request, g


UPLOAD_FOLDER = '/tmp'

app = Flask(__name__,
            static_url_path='',
            static_folder='static')

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER


@app.route("/upload", methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        try:           
            file = request.files['file']
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))

            transport = paramiko.Transport("sftphosthere", 22)
            try:
                transport.connect(username="usernamehere", password="passwordhere", hostkey=None)
                sftp = paramiko.SFTPClient.from_transport(transport)
            except paramiko.ssh_exception.AuthenticationException as e:
                return e
            path = '/tmp/'+file.filename
            try:
                sftp.put(os.path.join(app.config['UPLOAD_FOLDER'], filename), path)
                return "Sftp OK!"
            except Exception as e:
                return e

        except Exception as e:
            return e
    else:
        return "GET request"


############################################################################################################################
if __name__ == "__main__":
    app.run(debug=True, port=8000, host="0.0.0.0")

作为客户端发出POST请求,可以使用curl命令代替Postman:

curl -X POST http://flaskserverhere:8000/upload -F "file=@my_testfile.txt"

你需要在 运行 之前的同一目录中准备一个文件(上面命令中的my_testfile.txt)。

Flask save accepts not only a file path, but also a file-like object. To obtain a file-like object representing a file on an SFTP server, use Paramiko SFTPClient.open。这样您就可以将 HTTP-uploaded 文件直接传输到 SFTP 服务器,而不必将其临时存储在网络服务器上。

sftp = paramiko.SFTPClient.from_transport(transport)
path = '/home/'+file.filename
with sftp.open(path, 'w+', 32768) as f:
    file.save(f)

关于 32768 论点的目的,请参阅 Writing to a file on SFTP server opened using Paramiko/pysftp "open" method is slow


强制性警告:您直接使用 low-level Transport class 绕过 SSH/SFTP 服务器主机密钥验证(您甚至通过 hostkey=None).这是一个安全漏洞。对于正确的解决方案,请使用 SSHClient API 并查看 Paramiko "Unknown Server".