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