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