I am trying to send a file using a POST request, which is then forwarded to a server using paramiko. I have the following code:
@app.route("/", methods=['GET', 'POST'])
def upload():
file = request.files['file']
try:
gethostbyname("servername")
except gaierror:
print(gaierror)
exit()
port = 22
try:
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)
Unfortunately nothing is uploaded. I'm afraid I have to save the file that comes from the POST request first. Is it possible to work around this? Or am I missing something?
the key/value from my post request (using Postman):
key:file
value:test.txt
CodePudding user response:
Your code would benefit from some restructuring and clean up, actually looks like you removed some lines here and there.
The main issue is that you are trying to SFTP the file
from the POST argument, while
sftp.put
expects as first argument a filepath from the FS.
here is a re-organized function, that worked for me. You can notice that the file is first saved to the FS (file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
) and then this same string is passed to the 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")
as a client to issue the POST request, you can use the curl command instead of Postman:
curl -X POST http://flaskserverhere:8000/upload -F "file=@my_testfile.txt"
you need to prepare a file (my_testfile.txt
in the command above) in the same directory before running it.
CodePudding user response:
The Flask save
accepts not only a file path, but also a file-like object. To obtain a file-like object representing file on SFTP server, use Paramiko SFTPClient.open
. That way you will stream the uploaded file directly to the SFTP server, without having to store it temporarily on the web server.
sftp = paramiko.SFTPClient.from_transport(transport)
path = '/home/' file.filename
with sftp.open(path, 'w ', 32768) as f:
file.save(f)
For the purpose of the 32768
argument, see Writing to a file on SFTP server opened using Paramiko/pysftp "open" method is slow.
Obligatory warning: Your direct use of low-level Transport
class bypasses SSH/SFTP server host key verification (what you even enforce by hostkey=None
). That is a security flaw. For a correct solution, use SSHClient
API and see Paramiko "Unknown Server".