Android : uploader une image sur un serveur web

Une petite Mise à jour : pour écrire l’image pour envoi : utilisation de Bitmap.compress !
Ultra plus rapide !!!!

Un petit tuto pour apprendre à uploader une image (jpg ou autre) depuis votre application android vers un serveur web.
Pour ce genre de tâche qui peut être longue, Android conseille d’implémenter un Service plutôt qu’une activité : un service tourne en tâche de fond et ne bloque pas l’application.

Pour ce tuto :

  • créez une nouvelle application TutoUpload et une classe TutoUpload.
  • créez ensuite une deuxième classe HttpUploader
  • n’oubliez pas de déclarer ces classes dans le Manifest et d’ajouter la permission internet pour l’application
  • créez le fichier main.xml pour la layout de la classe TutoUpload

Le code de la classe TutoUpload :

package com.test.upload;

import java.io.File;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class TutoUpload extends Activity {

protected Uri image=null; //acces au fichier via contentResolver
File fichier; //le fichier à uploader

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

//un bouton pour envoi du fichier vers le serveur
final Button validbutton = (Button) findViewById(R.id.send);
validbutton.setOnClickListener(send_listener);
}

//pour valider tout pour envoi vers site web
OnClickListener send_listener = new OnClickListener() {
public void onClick(View v) {

image = Uri.fromFile(fichier);
Intent uploadIntent = new Intent( );
uploadIntent.setClassName("com.test.upload", "com.test.upload.HttpUploader");
uploadIntent.setData(image);
startService(uploadIntent);
}
};

@Override
protected void onPause() {
super.onPause();
}

@Override
protected void onStop() {
Log.i(getClass().getSimpleName(),"on stop");
super.onStop();
}

@Override
protected void onDestroy() {
super.onDestroy();
}
}

Le code du service HttpUploader

package com.test.upload;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.app.Service;
import android.content.ContentResolver;
import android.content.Intent;
import android.provider.MediaStore;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

public class HttpUploader extends Service {

private Intent mInvokeIntent;
private volatile Looper mUploadLooper;
private volatile ServiceHandler mUploadHandler;

private int check = 0;

private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}

@Override
public void handleMessage(Message msg) {
//get extra datas
Uri selectedImg = (Uri)msg.obj;
Log.i(getClass().getSimpleName(),"selectedImg =" + selectedImg);

//upload the file to the web server
doHttpUpload(selectedImg);

Log.i(getClass().getSimpleName(), "Message: " + msg);
Log.i(getClass().getSimpleName(), "Done with #" + msg.arg1);
stopSelf(msg.arg1);
}
};

// Method called when (an instance of) the Service is created
public void onCreate() {
Log.i(getClass().getSimpleName(),"HttpUploader on create");

// This is who should be launched if the user selects our persistent
// notification.
mInvokeIntent = new Intent();
mInvokeIntent.setClassName("com.test.upload", "com.test.upload.HttpUploader");

// Start up the thread running the service.  Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block.
HandlerThread thread = new HandlerThread("HttpUploader");
thread.start();

mUploadLooper = thread.getLooper();
mUploadHandler = new ServiceHandler(mUploadLooper);
}

public void onStart(Intent uploadintent, int startId) {
// recup des data pour envoi via msg dans la msgqueue pour traitement
Message msg = mUploadHandler.obtainMessage();
msg.arg1 = startId;
//on place l'uri reçu dans l'intent dans le msg pour le handler
msg.obj = uploadintent.getData();
mUploadHandler.sendMessage(msg);
Log.d(getClass().getSimpleName(), "Sending: " + msg);

}

public void doHttpUpload(Uri myImage) {
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
String photofile = null;
String httpResponse; //to read http response
String filename=null;

String urlString = "http://www.site.com/fileUpload.php";
HttpURLConnection conn = null;

InputStream fis = null;
Bitmap mBitmap=null;
String pathfile;

if (myImage != null) {
//on récupère le nom du fichier photo construit avec date et heure
filename = "photo.jpg";

String[] projection = { MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.DISPLAY_NAME};
ContentResolver cr = getContentResolver();
Cursor c = cr.query(myImage, projection, null, null, null);
if (c!=null && c.moveToFirst()) {
pathfile = c.getString(0); //column0Value
photofile = c.getString(1);     //column1Value
Log.i(getClass().getSimpleName(),"Data : " +pathfile);
Log.i(getClass().getSimpleName(),"Display name : " + photofile);
}

try {
fis = getContentResolver().openInputStream(myImage);
mBitmap = BitmapFactory.decodeStream(fis);

try {
int bytesAvailable = fis.available();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i(getClass().getSimpleName(),"échec de lecture de la photo");
stopSelf();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(HttpUploader.this, "échec de lecture de la photo ", Toast.LENGTH_SHORT).show();
Log.i(getClass().getSimpleName(),"échec de lecture de la photo");
stopSelf();
}

} else Log.i(getClass().getSimpleName(),"myImage is null");

try {
URL site = new URL(urlString);
conn = (HttpURLConnection) site.openConnection();

//on peut écrire et lire
conn.setDoOutput(true);
conn.setDoInput(true);

// Use a post method.
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary);

DataOutputStream dos = new DataOutputStream( conn.getOutputStream() );

dos.writeBytes(twoHyphens + boundary + lineEnd);
Log.i(getClass().getSimpleName(),"Display name : " + photofile);
Log.i(getClass().getSimpleName(),"Filename : " + filename);
dos.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + filename + "\"" + lineEnd);
dos.writeBytes(lineEnd);

Log.i(getClass().getSimpleName(),"Headers are written");

//compression de image pour envoi
mBitmap.compress(CompressFormat.JPEG, 75, dos);

// send multipart form data necesssary after file data...
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

// close streams
fis.close();
dos.flush();
dos.close();
Log.e("fileUpload","File is written on the queue");

} catch (MalformedURLException e) {
e.printStackTrace();
Toast.makeText(HttpUploader.this, "échec de connexion au site web ", Toast.LENGTH_SHORT).show();
Log.i(getClass().getSimpleName(),"échec de connexion au site web 1");
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(HttpUploader.this, "échec de connexion au site web ", Toast.LENGTH_SHORT).show();
Log.i(getClass().getSimpleName(),"échec de connexion au site web 2");
}

//lecture de la réponse http
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
Log.i(getClass().getSimpleName(),"try HTTP reponse");
while ((httpResponse = rd.readLine()) != null) {
Log.i(getClass().getSimpleName(),"HTTP reponse= " + httpResponse);
if(httpResponse.contains("error")) {
//there is a http error
check += 1;
}
}
rd.close();
} catch (IOException ioex){
Log.e("HttpUploader", "error: " + ioex.getMessage(), ioex);
ioex.printStackTrace();
Toast.makeText(HttpUploader.this, "échec de lecture de la réponse du site web ", Toast.LENGTH_SHORT).show();
Log.i(getClass().getSimpleName(),"échec de lecture de la réponse du site web");
}

}

// Method called when the (instance of) the Service is requested to terminate
public void onDestroy() {
mUploadLooper.quit();

if(check == 0) { //http response contains no error
Toast.makeText(HttpUploader.this, "photo envoyée", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(HttpUploader.this, "échec d'envoi de la photo", Toast.LENGTH_SHORT).show();
}
super.onDestroy();
}

@Override
public IBinder onBind(Intent intent) {
return null;
}
}

Le fichier de layout main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>

<Button
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10px"
android:text="Envoi" />
</LinearLayout>

A vous de jouer !

 

Pour répondre à la question de Marvin, voici le code PHP que j’utilise sur mon serveur pour récupérer l’image. Ce code permet également de redimensionner les images reçues pour un affichage ultérieru sur le téléphone en plus petit.

 


&lt;?php

// Where the file is going to be placed
$target_path =  $_SERVER['DOCUMENT_ROOT']."/uploads/";
$small_path= $_SERVER['DOCUMENT_ROOT']."/uploads/smallimg/";
//$target_path = "uploads/";

//Add the original filename to our target path.
$file = basename($_FILES['uploadedfile']['name']);

$target_path = $target_path.$file;
echo "target_path= ".$target_path." ";

//pour tester que c'est bien une image
$extensions = array('.png', '.gif', '.jpg', '.jpeg', '.JPG', '.JPEG');
$extension = strrchr($_FILES['uploadedfile']['name'], '.');
$ok = 0;

//check if the file is uploaded
if(in_array($extension, $extensions)) {

if(is_uploaded_file($_FILES['uploadedfile']['tmp_name'])) {
echo "Fichier= ". $_FILES['uploadedfile']['name'] ." telechargement OK.\n";

if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'],$target_path) === TRUE) {
echo "The file ".  basename( $_FILES['uploadedfile']['name']).
" has been uploaded";
$ok = 1;
} else {
echo "There was an error uploading the file, please try again!";
echo "error =".basename($_FILES['uploadedfile']['error']);
echo "file=".$_FILES['uploadedfile']['tmp_name'];
}

} else {
echo " Nom du fichier : '". $_FILES['uploadedfile']['name'];
}
} else {
echo "mauvaise extension de fichier";
}

if ($ok == 1) {
$small_path=$small_path.$file;
$img_path=$target_path;
$redimLargeur=300;
$redimHteur=300;

if(!file_exists($small_path)) {
/* creation de notre copie de travail de l'image */
$tmpImg = imagecreatefromjpeg($img_path);

/* recup des dimensions de l'image a redimensionner */
$tailleImg = getimagesize($img_path);

/* calcul des nouvelles dimensions de l'image */
if($tailleImg[0] &amp;amp;gt; $tailleImg[1]) {
/* format paysage */
$ratioReduc = (($redimLargeur * 100)/$tailleImg[0]);
$newHteur = (($tailleImg[1] * $ratioReduc)/100 );
$newLargeur = $redimLargeur;

} else {
/* format portrait */
$ratioReduc = (($redimHteur * 100)/$tailleImg[1]);
$newLargeur = (($tailleImg[0] * $ratioReduc)/100 );
$newHteur = $redimHteur;
}

/* creation de la nouvelle image en couleurs vraies */
$rszImg = imagecreatetruecolor($newLargeur ,$newHteur) or die ("Erreur");

/* reechantillonnage de l'image */
imagecopyresampled($rszImg , $tmpImg, 0, 0, 0, 0, $newLargeur, $newHteur, $tailleImg[0],$tailleImg[1]);

/* enregisttrement de l'image redimensionnée */
imagejpeg($rszImg , $small_path, 100);
}
}

?&gt;
This entry was posted in android and tagged , , . Bookmark the permalink.

20 Responses to Android : uploader une image sur un serveur web

  1. dewoo says:

    J’ai beaucoup apprécié ce tuto, très explicite et pertinence.

    Un petite question me taraude du coup, pour la récupération d’images, imaginons 5 en même temps, service ou thread/handler du coup ?

  2. flo says:

    Le Service te permet de faire tourner ta récup d’image en tache de fond sans bloquer ton appli. Si tu ne maîtrise pas trop, tu peux opter pour thread / hnadler mais en créant une thread qui gère le chargement des images pour pas que celà tourne sur ton UI thread (la thread principale) en bloquant ton appli.
    Si le chargement des images, c’est pour les afficher pour l’utilisateur, tu peux aussi les charger dans la thread principale avec une ProgressDialog qui dit chargement en cours.

  3. Coucal says:

    Je n’arrive pas à complier ce tutorial : Choosepicture n’est pas défini..
    Merci
    Alai,

  4. zobi8225 says:

    ennorme

  5. flo says:

    Coucal :
    une erreur de copié collé. Corrigé : filename est le nom du fichier à uploader.

  6. exico says:

    hi,
    what i should make the URL if i want make my Laptop like a server

  7. Sami.k says:

    hi,
    moi aussi je n’arrive pa à faire tourné ce code,
    @exico : I think u should make : http://10.0.2.2:Numport/Folder

  8. berrada says:

    j’aime et je bookmark, merci pour cet article

  9. marvin says:

    ça ne fonctionne pas, lors de l’appui sur “Envoi”, l’appli se crash systématiquement.

  10. flo says:

    avec quelques log je pourrai aider au debug….. cela fonctionne tres bein dans mon appli

  11. marvin says:

    Salut, voici le maximum que j’ai pu trouver.
    En résumé, je me suis contenté de mettre les .java où il faut, et de declarer la permission et le service dans le manifest.
    Je lance, j’appuie sur Envoi et l’appli crash.
    (ai-je oublié quelque chose?? comment spécifier le fichier à envoyer??)

    Voici ma pile :

    DalvikVM[localhost:8621]
    Thread [ main] (Suspended (exception NullPointerException))
    ViewRoot.handleMessage(Message) line: 1740
    ViewRoot(Handler).dispatchMessage(Message) line: 99
    Looper.loop() line: 123
    ActivityThread.main(String[]) line: 4363
    Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]
    Method.invoke(Object, Object…) line: 521
    ZygoteInit$MethodAndArgsCaller.run() line: 860
    ZygoteInit.main(String[]) line: 618
    NativeStart.main(String[]) line: not available [native method]
    Thread [ Binder Thread #2] (Running)
    Thread [ Binder Thread #1] (Running)

    Le logcat ne donne aucune info lors d ce click, la console de debug non plus.

    J’ai hate de pouvoir faire tourner ce code, tu as l’air d’avoir fait un gros travail. ^^

    PS : Tu devrais peut etre également publier le manifest.xml. La phrase “n’oubliez pas de déclarer ces classes dans le Manifest” est assez abstraite pour un débutant. :S

  12. Bzul says:

    Ça a l’air sympa, mais même question que marvin, comment spécifier le chemin du fichier ?

  13. flo says:

    désolée pour le temps de réponse, je bosse plus sur des applis iPhone en ce moment….
    pour préciser le fichier à uploader c’est en ligne 16 du code. Il faut un peut compléter le code en construisant le nom de fcihier avec le path complet. Pour vous aider, un peu de doc : http://developer.android.com/intl/de/reference/java/io/File.html

    PS : concernant le fichier manifest.xml, je ne l’ai pas publié et je ne pense pas le faire car il me semble qu’un débutant devrait en premier lieu lire la doc android.
    Le fichier manifest.xml est une base très importante des applis, la doc explique très bien comment le construire et donc comment déclarer chaque classe dans ce fichier. La doc : http://developer.android.com/intl/de/guide/topics/manifest/manifest-intro.html

  14. iphone 4 says:

    A formidable share, I just given this onto a colleague who was doing a bit of analysis on this. And he in actual fact bought me breakfast as a result of I discovered it for him.. smile. So let me reword that: Thnx for the treat! But yeah Thnkx for spending the time to discuss this, I feel strongly about it and love reading extra on this topic. If attainable, as you grow to be experience, would you thoughts updating your weblog with extra details? It is highly helpful for me. Huge thumb up for this blog put up!

  15. pikos says:

    bonsoir tout le monde
    merci bien pour le code !!! excellent
    j’ai la même question que marvin “merçi pour la question maven”
    mais j’ai quelque problème coté serveur. j’ai pas arrivé a exécuté un code d’upload files “en PHP” car les méthode input d’un formulaire HTML ne fonctionne pas sur un terminal mobile car le bouton de parcourir pour choisir le fichier a uploader devin grisé “selon les test, mes connaissances et mes recherche sur le net bien-sur ! ,en plus j’aimerai mieux que cette opération d’upload soit avec un clic simple sur un bouton et non pas avec parcourir … car le fichier a étai générer automatiquement par une méthode
    cordialement PiKoS

  16. Marvin says:

    Je repasse ici pour tester ce problème maintenant que j’ai un peu plus d’expérience.
    J’arrive à faire marcher le code Android.

    Par contre coté serveur je ne reçois rien à première vu.
    Où sont censées arriver les données ?

    je n’ai rien dans les variables PHP suivantes :
    - $_POST
    - $_GET
    - $_FILE
    - $HTTP_RAW_POST_DATA

    Comme ça se fait ?

  17. flo says:

    @Marvin : J’ai ajouté en fin du tuto le code PHP que j’utilise pour uploader l’image côté serveur. j’espère que celà t’aidera !

  18. Stef says:

    Ca marche nickel dans mon programme Android. Merci !!!

  19. Raphi says:

    Merci pour ce tuto ! Marche très bien !

  20. monta says:

    slt,
    c’est tres interesant,
    mais ma question c’est comment faire si je veux envoyer des données se trouve dans une listview android vers mon serveur pour récupérer ces variables (nom, prix,description) dans la base
    svp aider moi
    j’attend votre reponse

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>