Enviando arquivos e dados ao mesmo tempo via HTTP POST usando UTF-8

Um problema que sempre enfrento é enviar arquivos e dados (parâmetros, variáveis) usando python via HTTP POST. Existem soluções para enviar somente arquivos ou somente parâmetros que servem para a maior parte do uso, mas não para o meu. Um é exemplo enviar uma foto com título e descrição. Uma das únicas boas referências que eu achei foi este post no ActiveState feito em 2002, mas o problema é que ele não funcionar com UTF-8.

Este post é para mostrar um código que funciona para o envio de arquivos e parâmetros ao mesmo tempo em qualquer codificação.

In english:

One problem I ever face is when sending files and data (parameters, variables) using python via HTTP POST.

There are solutions to send only files or only parameters that are suitable for most use, but not mine. One example is sending a photo with title and description. One of the only good reference I found was this post on ActiveState made ​​in 2002, but the problem is that it does not work with UTF-8.

This post is to show a code that works for sending files and parameters simultaneously in any encoding.

Abaixo segue o código:

import httplib
import mimetypes
import base64

def post_multipart(host, selector, fields, files, encoding='utf-8'):
    """
    Post fields and files to an http host as multipart/form-data.
    fields is a sequence of (name, value) elements for regular form fields.
    files is a sequence of (name, filename, value) elements for data to be uploaded as files
    Return the server's response page.
    """
    content_type, body = encode_multipart_formdata(fields, files)
    body = body.encode(encoding)

    h = httplib.HTTP(host)
    h.putrequest(u'POST', selector)
    h.putheader(u'Content-Type', content_type)
    h.putheader(u'Charset', encoding)
    h.putheader(u'Content-Length', str(len(body)))
    h.endheaders()
    h.send(body)

    errcode, errmsg, headers = h.getreply()
    return h.file.read()

def encode_multipart_formdata(fields, files, encoding='utf-8'):
    """
    fields is a sequence of (name, value) elements for regular form fields.
    files is a sequence of (name, filename, value) elements for data to be uploaded as files
    Return (content_type, body) ready for httplib.HTTP instance
    """
    BOUNDARY = u'----------ThIs_Is_tHe_bouNdaRY_$'
    CRLF = u'rn'
    L = []

    for (key, value) in fields:
        L.append(u'--' + BOUNDARY)
        L.append(u'Content-Disposition: form-data; name="%s"' % key)
        L.append(u'Content-Type: text/plain;charset=%s' % encoding)
        L.append(u'Content-Transfer-Encoding: 8bit')
        L.append(u'')
        L.append(value)

    for (key, filename, value) in files:
        L.append(u'--' + BOUNDARY)
        L.append(u'Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
        L.append(u'Content-Type: %s;charset=%s' % (get_content_type(filename), encoding))
        L.append(u'Content-Transfer-Encoding: base64')
        L.append(u'')
        L.append(base64.b64encode(value).decode())

    L.append(u'--' + BOUNDARY + u'--')
    L.append(u'')
    body = CRLF.join(L)
    content_type = u'multipart/form-data; boundary=%s' % BOUNDARY

    return content_type, body

def get_content_type(filename):
    return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

As diferenças entro o meu código e o código original são:

  • Uso de strings unicode e a codificação é feita apenas no momento do envio da requisição.
  • Uso da variável encoding para definir o encoding que será enviada a requisição.
  • Codificando o arquivo em base64 para não ter problemas com acentuação interna.
  • Indicando no cabeçalho do boundary que o arquivo está codificado em base64.
  • Adicionando Charset no cabeçalho da requisição e nos cabeçalhos do boundary.
Na variável encoding você pode usar UTF-8, ISO-8859-1 (latin1) ou qualquer outra codificação, desde que o suas strings sejam sempre unicode, ou seja, o valor das seus parâmetros deve ser unicode.

Tocando som com o Arduino

A forma mais simples de reproduzir som no Arduino é usando um Buzzer ou um Piezo. O Buzzer é muito usado em computadores para emitir o sinal inicialização do sistema e para indicar possíveis erros. Quem nunca ouviu os 3 bips infernais de erro de memória e teve que ficar o final de semana inteiro sem computador? Pois é, é pelo Buzzer que esse sinal sonoro é emitido. Continue reading

Arduino

Comprei recentemente um Arduino. Comecei a brincar primeiro com com leds e resistores e tudo é muito divertido, mesmo acender um led que é uma coisa muito simples traz uma enorme satisfação quando você faz tudo, desde o programa até o circuito.

Eu não sabia nada de eletrônica, e ainda não sei muito, mas é muito legal quando você tem que pensar sobre os componentes que farão parte do seu circuito e se você deve ou não colocar um resistor alí entre eles. Isso faz você aprender bastante na prática.

Neste blog vou tentar falar sobre Arduino e quando eu for desenvolvendo meus projetos colocarei em detalhes aqui.

Até mais.

PS: Tentarei colocar todas as imagens, diagramas e vídeos em alta definição para ficar mais simples de acompanhar o que e como estou fazendo.