python solicita upload de arquivo

123

Estou executando uma tarefa simples de upload de um arquivo usando a biblioteca de solicitações Python. Pesquisei Stack Overflow e ninguém parecia ter o mesmo problema, ou seja, que o arquivo não é recebido pelo servidor:

import requests
url='http://nesssi.cacr.caltech.edu/cgi-bin/getmulticonedb_release2.cgi/post'
files={'files': open('file.txt','rb')}
values={'upload_file' : 'file.txt' , 'DB':'photcat' , 'OUT':'csv' , 'SHORT':'short'}
r=requests.post(url,files=files,data=values)

Estou preenchendo o valor da palavra-chave 'upload_file' com meu nome de arquivo, porque se eu deixar em branco, ele diz

Error - You must select a file to upload!

E agora eu consigo

File  file.txt  of size    bytes is  uploaded successfully!
Query service results:  There were 0 lines.

Que só aparece se o arquivo estiver vazio. Então, estou sem saber como enviar meu arquivo com sucesso. Eu sei que o arquivo funciona porque se eu entrar neste site e preencher manualmente o formulário, ele retorna uma boa lista de objetos correspondentes, que é o que procuro. Eu realmente aprecio todas as dicas.

Alguns outros tópicos relacionados (mas não respondem ao meu problema):

Scichris
fonte

Respostas:

210

Se upload_filefor o arquivo, use:

files = {'upload_file': open('file.txt','rb')}
values = {'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'}

r = requests.post(url, files=files, data=values)

e requestsenviará um corpo POST de formulário com várias partes com o upload_filecampo definido para o conteúdo do file.txtarquivo.

O nome do arquivo será incluído no cabeçalho mime do campo específico:

>>> import requests
>>> open('file.txt', 'wb')  # create an empty demo file
<_io.BufferedWriter name='file.txt'>
>>> files = {'upload_file': open('file.txt', 'rb')}
>>> print(requests.Request('POST', 'http://example.com', files=files).prepare().body.decode('ascii'))
--c226ce13d09842658ffbd31e0563c6bd
Content-Disposition: form-data; name="upload_file"; filename="file.txt"


--c226ce13d09842658ffbd31e0563c6bd--

Observe o filename="file.txt"parâmetro.

Você pode usar uma tupla para o filesvalor de mapeamento, com entre 2 e 4 elementos, se precisar de mais controle. O primeiro elemento é o nome do arquivo, seguido pelo conteúdo e um valor de cabeçalho opcional do tipo de conteúdo e um mapeamento opcional de cabeçalhos adicionais:

files = {'upload_file': ('foobar.txt', open('file.txt','rb'), 'text/x-spam')}

Isso define um nome de arquivo alternativo e tipo de conteúdo, deixando de fora os cabeçalhos opcionais.

Se você está querendo que todo o corpo do POST seja obtido de um arquivo (sem outros campos especificados), não use o filesparâmetro, apenas poste o arquivo diretamente como data. Você também pode querer definir um Content-Typecabeçalho, pois nenhum será definido de outra forma. Consulte solicitações Python - dados POST de um arquivo .

Martijn Pieters
fonte
Olá, Como faço para enviar vários arquivos com o mesmo nome? Como 'anexo', por exemplo.
William Wino
4
@William: você pode usar uma seqüência de tuplas 2 de valor também, o que permite reutilizar nomes de campos: files = [('attachment', open('attachment1.txt', 'rb')), ('attachment', open('attachment2.txt', 'rb'))]. Cada tupla é um par de chave e valor.
Martijn Pieters
2
Além disso, você também pode usar, files={'file':('nameoffile',open('namoffile','rb'),'Content-Type':'text/html','other header'),'file2':('nameoffile2',open('nameoffile2','rb'),'Content-Type':'application/xml','other header')}mas se files = {} for usado, headers = {'Content-Type': 'blah blah'} não deve ser usado! -> @ martijn-pieters: porque o tipo de conteúdo multipart / form-data deve incluir o valor limite usado para delinear as partes no corpo da postagem. Não definir o cabeçalho Content-Type garante que as solicitações o definam com o valor correto.
zaki de
1
@MartijnPieters Isso não corre o risco de vazar o arquivo? Fecha requests?
Matt Messersmith
4
@MattMessersmith: não, não está fechado. Se você deseja fechar o arquivo, use with open(...) as fobj:e use fobjno filesmapeamento.
Martijn Pieters
35

(2018) a nova biblioteca de solicitações python simplificou esse processo, podemos usar a variável 'arquivos' para sinalizar que queremos fazer o upload de um arquivo codificado em várias partes

url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

r = requests.post(url, files=files)
r.text
leigo
fonte
3
A biblioteca de solicitações fecha o arquivo automaticamente?
Demetris
1
Olá, já faz algum tempo desde que usei esta biblioteca. boa pergunta. você poderia ajudar a mim e aos outros digitando lsof | grep "filename" e compartilha seus resultados conosco? obrigado :)
laycat
1
Com o uso de lsof, parece que o arquivo permanece aberto, ou pelo menos é assim que interpreto os seguintes resultados. Antes, rodando o opennão há registro na lsoftabela sobre o filename. Depois de openexecutado, vários registros aparecem com readacesso. Após a execução do requests.post, os registros continuam lá indicando que o arquivo não foi fechado.
Demetris
23

Upload de cliente

Se você deseja fazer upload de um único arquivo com a requestsbiblioteca Python , então o request lib suporta uploads de streaming , que permitem enviar arquivos grandes ou fluxos sem ler na memória .

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

Lado do Servidor

Em seguida, armazene o arquivo server.pylateralmente de forma que salve o fluxo em um arquivo sem carregar na memória. A seguir está um exemplo com o uso de uploads de arquivo Flask .

@app.route("/upload", methods=['POST'])
def upload_file():
    from werkzeug.datastructures import FileStorage
    FileStorage(request.stream).save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    return 'OK', 200

Ou use a análise de dados de formulário werkzeug, conforme mencionado em uma correção para o problema de " uploads de arquivos grandes consumindo memória ", a fim de evitar o uso ineficiente da memória no upload de arquivos grandes ( arquivo st 22 GiB em ~ 60 segundos. O uso de memória é constante em cerca de 13 MiB.).

@app.route("/upload", methods=['POST'])
def upload_file():
    def custom_stream_factory(total_content_length, filename, content_type, content_length=None):
        import tempfile
        tmpfile = tempfile.NamedTemporaryFile('wb+', prefix='flaskapp', suffix='.nc')
        app.logger.info("start receiving file ... filename => " + str(tmpfile.name))
        return tmpfile

    import werkzeug, flask
    stream, form, files = werkzeug.formparser.parse_form_data(flask.request.environ, stream_factory=custom_stream_factory)
    for fil in files.values():
        app.logger.info(" ".join(["saved form name", fil.name, "submitted as", fil.filename, "to temporary file", fil.stream.name]))
        # Do whatever with stored file at `fil.stream.name`
    return 'OK', 200
Gihanchanuka
fonte
0

No Ubuntu você pode aplicar desta forma,

para salvar o arquivo em algum local (temporário) e depois abri-lo e enviá-lo para a API

      path = default_storage.save('static/tmp/' + f1.name, ContentFile(f1.read()))
      path12 = os.path.join(os.getcwd(), "static/tmp/" + f1.name)
      data={} #can be anything u want to pass along with File
      file1 = open(path12, 'rb')
      header = {"Content-Disposition": "attachment; filename=" + f1.name, "Authorization": "JWT " + token}
       res= requests.post(url,data,header)
Harshit Trivedi
fonte
qual é o valor da datavariável?
am.rez
pode ser qualquer coisa como nome de usuário, acabei de mostrar como fazer upload de arquivos para REST apis
Harshit Trivedi