Esse post tava há um bom tempo como rascunho no blog e só agora consegui terminar para postar aqui. Vou mostrar como fazer uma aplicação Android que faz upload de arquivos para um servidor web.
public class UploadActivity extends Activity {
  private TextView txtArquivo;
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_upload);
    txtArquivo = (TextView)
      findViewById(R.id.txtArquivo);
  }
 
  // Ao selecionar o arquivo, preenchemos o 
  // TextView com o caminho real do arquivo
  @Override
  protected void onActivityResult(int requestCode, 
    int resultCode, Intent data) {
    super.onActivityResult(requestCode, 
      resultCode, data);
    if (resultCode == RESULT_OK) {
      if (requestCode == 0) {
        Uri selectedImageUri = data.getData();
        txtArquivo.setText(getPath(selectedImageUri));
      }
    }  
  }
  // Botão que abre a galeria de mídia para 
  // selecionar um arquivo
  public void selecionarArquivo(View v){
    Intent intent = new Intent();
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(Intent.createChooser(
      intent,"Selecione uma imagem"), 0);
  }
 
  // Botão que envia o arquivo selecionado
  public void enviarArquivo(View v){
    try {
      new Thread(){
        public void run(){
          executeMultipartPost();
        }
      }.start();
    } catch (Exception e) {
      Log.e("NGVL", e.getMessage());
    }  
  }
 
  // Obtém o caminho real do arquivo 
  // através do ContentProvider
  public String getPath(Uri uri) {
    String[] projection = { 
      MediaStore.Images.Media.DATA };
    Cursor cursor = getContentResolver().query(
      uri, projection, null, null, null);
    int column_index = cursor.getColumnIndexOrThrow(
      MediaStore.Images.Media.DATA);
    cursor.moveToFirst();
    return cursor.getString(column_index);
  }
 
  // Método que realmente envia o arquivo
  public void executeMultipartPost() 
    throws Exception {
    String caminhoDoArquivoNoDispositivo = 
      txtArquivo.getText().toString();
    String urlDoServidor = 
      "http://url_servidor/pagina_que_trata_upload";
    String lineEnd = "\r\n";
    String twoHyphens = "--";
    String boundary = "*****"; // Delimitador
    byte[] buffer;
    int bytesRead, bytesAvailable, bufferSize;
    int maxBufferSize = 1 * 1024 * 1024; // 1MB
    try {
      // Criando conexão com o servidor
      URL url = new URL(urlDoServidor);
      HttpURLConnection connection = 
        (HttpURLConnection) url.openConnection();
      // Conexão vai ler e escrever dados
      connection.setDoInput(true);
      connection.setDoOutput(true);
      connection.setUseCaches(false);
      // Setando método POST
      connection.setRequestMethod("POST");
      // Adicionando cabeçalhos
      connection.setRequestProperty(
        "Connection", "Keep-Alive");
      connection.setRequestProperty(
        "Content-Type",
        "multipart/form-data;boundary=" + boundary);
      connection.connect();
   
      // Escrevendo payload da requisição
      DataOutputStream outputStream = 
        new DataOutputStream(
          connection.getOutputStream());
      outputStream.writeBytes(
        twoHyphens + boundary + lineEnd);
      outputStream.writeBytes(
        "Content-Disposition: form-data; "+
        "name=\"uploadedfile\";filename=\""+ 
        caminhoDoArquivoNoDispositivo + "\"" +
        lineEnd);
      outputStream.writeBytes(lineEnd);
      // Stream para ler o arquivo
      FileInputStream fileInputStream = 
        new FileInputStream(new File(
          caminhoDoArquivoNoDispositivo));
   
      // Preparando para escrever arquivo
      bytesAvailable = fileInputStream.available();
      bufferSize = Math.min(
        bytesAvailable, maxBufferSize);
      buffer = new byte[bufferSize];
      // Lendo arquivo e escrevendo na conexão
      bytesRead = fileInputStream.read(
        buffer, 0, bufferSize);
      while (bytesRead > 0) {
        outputStream.write(buffer, 0, bufferSize);
        bytesAvailable = fileInputStream.available();
        bufferSize = Math.min(
          bytesAvailable, maxBufferSize);
        bytesRead = fileInputStream.read(
          buffer, 0, bufferSize);
      }
      outputStream.writeBytes(lineEnd);
      outputStream.writeBytes(
        twoHyphens + boundary + 
        twoHyphens + lineEnd);
      // Obtendo o código e a mensagem
      // de resposta do servidor
      int serverResponseCode = 
        connection.getResponseCode();
      String serverResponseMessage = 
        connection.getResponseMessage();
      Log.d("NGVL", serverResponseCode +" = "+ 
        serverResponseMessage);
   
      fileInputStream.close();
      outputStream.flush();
      outputStream.close();
   
    } catch (Exception ex) {
      // Exception handling
    }
  }
}
O código acima está todo comentado, então vou mostrar o arquivo de layout da aplicação. O único detalhe que eu quero ressaltar, é que utilizei uma Thread apenas pra facilitar o código, mas deveríamos utilizar uma AsyncTask ou até um Service.<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
  <TextView
    android:id="@+id/txtArquivo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Arquivo a ser enviado" />
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Selecionar arquivo" 
    android:onClick="selecionarArquivo"/>
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Enviar" 
    android:onClick="enviarArquivo"/>
</LinearLayout>
A última coisa a fazer é adicionar a permissão de INTERNET no AndroidManifest.xml.
<uses-permission android:name="android.permission.INTERNET"/>A figura abaixo mostra a aplicação em execução:
Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber

 
 
13 comentários:
Muito bom seu post! Só fique com uma dúvida: como eu envio imagem junto com um formulário?
Tks
Oi João Paulo,
Como é um multi-part/form-data, cada campo seria uma part. Então teria que escrever um campo dentro do boundary do request. Mais ou menos assim:
outputStream.outputStream.writeBytes("Content-Disposition: form-data; name=\"seucampo\""+ lineEnd + twoHyplineEnd +"valorDoCampo);
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(
twoHyphens + boundary +
twoHyphens + lineEnd);
4br4ç05s,
nglauber
Mais uma vez, obrigado! Consegui fazer o upload da imagem junto com o formulário!!!
Tks!!!
Muito bom o artigo mesmo.
Porém, e se eu quiser fazer um upload de um arquivo qualquer e não de uma imagem, o que eu precisaria mudar?
oi Thiaguinho,
Basicamente o que mudaria:
1) No método selecionarArquivo, o tipo seria */*
2) No método getPath teria que pesquisar como obter o caminho do arquivo selecionado
3) Usei a classe Thread nesse exemplo, mas o ideal é usar um IntentService ou uma AsyncTask dependendo do tamanho do download.
4br4ç05,
nglauber
Oi Glauber, tenho um serviço web no qual passo via post a imagem a ser upada, posso usar esse mesmo serviço para o código apresentado ??
Oi Rafael,
Teoricamente, pode sim... Mas aí depende de como o serviço tá implementado.
4br4ç05,
nglauber
E se fosse para enviar via Bluetooth, como seria ?
Abraços.
Oi Isaac,
A abordagem seria completamente diferente, pois são protocolos diferentes em vários aspectos.
No meu livro tem um capítulo sobre Bluetooth que pode ajudar e que é basicamente este artigo aqui que eu escrevi para a DevMedia.
http://www.devmedia.com.br/resumo/?ed=35&site=5
Se preferir, consulte a documentação oficial:
http://developer.android.com/guide/topics/connectivity/bluetooth.html
4br4ç05,
nglauber
Olá N.Glauber, primeiramente ótimo tutorial, ja fiz esse exemplo utilizando seu livro mas perdi meu app e agora estou refazendo ele. Porém me veio uma dúvida tem como mostrar o progresso de upload? Já tenho um ProgressBar e o layout tudo certinho mas não faço a minima ideia de como pegar o progresso de upload.
Oi Bruno,
A maneira mais simples é você usar a AsyncTask e no "while" do método executeMultipartPost() chamar o publishProgress(valor). Dá uma olhada nisso que vai te ajudar.
https://developer.android.com/reference/android/os/AsyncTask.html
4br4ç05,
nglauber
Sim sim, eu já uso uma AsyncTask, o problema é que como sou leigo não sei o que usar como valor no publishProgress(?), poderia dar um exemplo, se não for pedir muito.
Oi Bruno,
Você abriu o link que eu mandei? Nele tem um exemplo...
https://developer.android.com/reference/android/os/AsyncTask.html
No publishProgress você pode passar qualquer valor (dependendo de como você definiu a task), mas no seu caso, você passará quantos bytes já foram enviados e o total de bytes. Por exemplo:
int contadorBytes = 0;
while (bytesRead > 0) {
...
contadorBytes += bytesRead;
publishProgress(contadorBytes, bytesAvailable);
}
E no onProgress() você atualiza esse progresso de alguma forma:
protected void onProgressUpdate(Integer... progress) {
// progress[0] é o contadorBytes
// progress[1] é o total de bytes
}
Lembrando que sua AsyncTask tem que estar anotada (neste caso) como AsyncTask
4br4ç05,
nglauber
Postar um comentário