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.
<?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;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);
}
}
?>
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 ?
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.
Je n’arrive pas à complier ce tutorial : Choosepicture n’est pas défini..
Merci
Alai,
ennorme
Coucal :
une erreur de copié collé. Corrigé : filename est le nom du fichier à uploader.
hi,
what i should make the URL if i want make my Laptop like a server
hi,
moi aussi je n’arrive pa à faire tourné ce code,
@exico : I think u should make : http://10.0.2.2:Numport/Folder
j’aime et je bookmark, merci pour cet article
ça ne fonctionne pas, lors de l’appui sur “Envoi”, l’appli se crash systématiquement.
avec quelques log je pourrai aider au debug….. cela fonctionne tres bein dans mon appli
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
Ça a l’air sympa, mais même question que marvin, comment spécifier le chemin du fichier ?
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
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!
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
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 ?
@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 !
Ca marche nickel dans mon programme Android. Merci !!!
Merci pour ce tuto ! Marche très bien !
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