Today I'm trying to upload and image to a server. I found that a lot of developers recommend the use of HttpURLConnection. The performance is very important for me, so I tried to optimize the code the most I know how to do it. The problem I faced is that the uploaded images are considerably larger than the original ones.
If anyone sees something that can be done better, it would be of great help
This is my class to upload a bitmap:
private class SubirImagen extends AsyncTask<Bitmap, Integer, APIResponse<String>> {
String token;
String boundary = "***" + System.currentTimeMillis() + "***";
String crlf = "\r\n";
String twoHyphens = "--";
public SubirImagen(String token) {
this.token = token;
}
#Override
protected void onPostExecute(APIResponse<String> stringAPIResponse) {
progressBar.setVisibility(View.INVISIBLE);
if (stringAPIResponse.getErrors().isEmpty()) {
new File(pathToFile.getPath()).delete();
imagenCambiada = false;
pathToFile = null;
controler.showToast(getContext(), stringAPIResponse.getData());
} else {
controler.showToast(getContext(), stringAPIResponse.getErrors().get(0).getErrorMsg());
}
super.onPostExecute(stringAPIResponse);
}
#Override
protected void onPreExecute() {
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... values) {
progressBar.setProgress(values[0]);
}
#Override
protected APIResponse<String> doInBackground(Bitmap... bitmaps) {
ByteArrayOutputStream bao1 = new ByteArrayOutputStream();
bitmaps[0].compress(Bitmap.CompressFormat.JPEG, 100, bao1);
byte[] ba1 = bao1.toByteArray();
int size = (twoHyphens.length() * 3) + (boundary.length() * 2) + (crlf.length() * 5) +
("Content-Disposition: form-data; name=\"imagen\";filename=\"imagen\"".length()) +
ba1.length;
HttpURLConnection urlConnection = null;
try {
URL url = new URL("http://192.168.137.1:8080/BochoGameAPI/usuario/uploadFotoPerfil");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestProperty("Authorization", token);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Connection", "Keep-Alive");
urlConnection.setRequestProperty("Cache-Control", "no-cache");
urlConnection.setRequestProperty("ENCTYPE", "multipart/form-data");
urlConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
urlConnection.setFixedLengthStreamingMode(size);
DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream());
request.writeBytes(twoHyphens + boundary + crlf);
request.writeBytes("Content-Disposition: form-data; name=\"imagen\";filename=\"imagen\"" + crlf);
request.writeBytes(crlf);
request.flush();
int progress = 0;
int bytesRead;
byte buf[] = new byte[1024];
BufferedInputStream bufInput = new BufferedInputStream(new ByteArrayInputStream(ba1));
while ((bytesRead = bufInput.read(buf)) != -1) {
// write output
request.write(buf, 0, bytesRead);
request.flush();
progress += bytesRead;
// update progress bar
publishProgress((progress * 100) / size);
}
request.write(ba1);
request.writeBytes(crlf);
request.writeBytes(twoHyphens + boundary + twoHyphens + crlf);
request.flush();
request.close();
int code = urlConnection.getResponseCode();
if (code != 200) {
throw new IOException("Invalid response from server: " + code);
}
BufferedReader rd = new BufferedReader(new InputStreamReader(
urlConnection.getInputStream()));
String line;
Gson gson = new Gson();
Type typeToken = new TypeToken<APIResponse<String>>() {
}.getType();
APIResponse<String> apiResponse = null;
while ((line = rd.readLine()) != null) {
apiResponse = gson.fromJson(line, typeToken);
}
rd.close();
return apiResponse;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
}
Original image, 1.57 mb:
Uploaded image, 4.19 mb:
Related
I wanna send a file and some variables to server, to insert in a table in a android app using AsyncTask. here is my java code:
try {
URL url = new URL(upLoadServerUri);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
httpURLConnection.setUseCaches(false);
OutputStream outputStream = httpURLConnection.getOutputStream();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
String post_data= URLEncoder.encode("username" , "UTF-8")+"="+URLEncoder.encode(username,"UTF-8");
bufferedWriter.write(post_data);
bufferedWriter.flush();
bufferedWriter.close();
outputStream.close();
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1"));
result="";
String line="";
while ((line=bufferedReader.readLine()) != null){
result += line;
}
bufferedReader.close();
inputStream.close();
httpURLConnection.disconnect();
}catch (MalformedURLException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
try {
FileInputStream fileInputStream = new FileInputStream(sourceFile);
URL url = new URL(upLoadServerUri);
// Open a HTTP connection to the URL
conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true); // Allow Inputs
conn.setDoOutput(true); // Allow Outputs
conn.setUseCaches(false); // Don't use a Cached Copy
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("ENCTYPE",
"multipart/form-data");
conn.setRequestProperty("Content-Type",
"multipart/form-data;boundary=" + boundary);
conn.setRequestProperty("bill", sourceFileUri);
dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"bill\";filename=\""
+ sourceFileUri + "\"" + lineEnd);
dos.writeBytes(lineEnd);
// create a buffer of maximum size
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0,
bufferSize);
}
// send multipart form data necesssary after file
// data...
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// Responses from the server (code and message)
serverResponseCode = conn.getResponseCode();
String serverResponseMessage = conn
.getResponseMessage();
// close the streams //
fileInputStream.close();
dos.flush();
dos.close();
return result;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
And PHP code:
if (is_uploaded_file($_FILES['bill']['tmp_name'])) {
$uploads_dir = './sensorLog/$user_name';
$tmp_name = $_FILES['bill']['tmp_name'];
$pic_name = $_FILES['bill']['name'];
move_uploaded_file($tmp_name, $uploads_dir.$pic_name);
if($conn->query($mysql_qry) === TRUE) {
echo "upload and update successful";
}else { echo "Error" . $mysql_qry . "<br>" . $conn->error;}
}else{ echo "File not uploaded successfully."; }
I use two httpconnection object and I think this is the problem. How can I send variables and file simultaneously? this code upload the file to server, but the table isn't updated. it just add an empty row:(.
Use this class to send multipart requests:
class MultipartUtility {
public HttpURLConnection httpConn;
public OutputStream request;//if you wanna send anything out of ordinary use this;
private final String boundary = "*****";//alter this as you wish
private final String crlf = "\r\n";
private final String twoHyphens = "--";
public MultipartUtility(String requestURL)
throws IOException {
URL url = new URL(requestURL);
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setUseCaches(false);
httpConn.setDoOutput(true);
httpConn.setDoInput(true);
httpConn.setRequestMethod("POST");
//alter this part if you wanna set any headers or something
httpConn.setRequestProperty("Connection", "Keep-Alive");
httpConn.setRequestProperty("Cache-Control", "no-cache");
httpConn.setRequestProperty(
"Content-Type", "multipart/form-data;boundary=" + this.boundary);
request = httpConn.getOutputStream();
}
//or add some other constructor to better fit your needs; like set cookies and stuff
public void addFormField(String name, String value)throws IOException {
request.write(( this.twoHyphens + this.boundary + this.crlf).getBytes());
request.write(("Content-Disposition: form-data; name=\"" + name + "\""+ this.crlf).getBytes());
request.write(this.crlf.getBytes());
request.write((value).getBytes());
request.write(this.crlf.getBytes());
request.flush();
}
public void addFilePart(String fieldName, File uploadFile)
throws IOException,Exception {
if(uploadFile.isDirectory())throw new Exception("for real? what do you expect to happen?");
request.write((this.twoHyphens + this.boundary + this.crlf).getBytes());
request.write(("Content-Disposition: form-data; name=\"" +
fieldName + "\";filename=\"" +
uploadFile.getName()+ "\"" + this.crlf).getBytes());
request.write(this.crlf.getBytes());
InputStream is=new FileInputStream(uploadFile);
byte[] bytes = new byte[1024];
int c=is.read(bytes);
while(c>0){
request.write(bytes,0,c);
c=is.read(bytes);
}
request.write(this.crlf.getBytes());
request.flush();
is.close();
}
public String finish() throws IOException {
String response ="";
request.write((this.twoHyphens + this.boundary +
this.twoHyphens + this.crlf).getBytes());
request.flush();
request.close();
int status = httpConn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
InputStream responseStream = httpConn.getInputStream();
byte[] b=new byte[1024];
int c=responseStream.read(b);
while(c>0){
response=response+new String(b,0,c);
c=responseStream.read(b);
}
responseStream.close();
} else {
httpConn.disconnect();
throw new IOException("Server returned non-OK status: " + status);
}
httpConn.disconnect();
return response;
}
public InputStream finish_with_inputstream()throws Exception{
request.write((this.twoHyphens + this.boundary +
this.twoHyphens + this.crlf).getBytes());
request.flush();
request.close();
int status = httpConn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
return httpConn.getInputStream();
} else {
throw new IOException("Server returned non-OK status: " + status);
}
}
}
example:
try{
MultipartUtility m=new MultipartUtility("https://mydomain");//now add you different data parts;
m.addFilePart("file_part",new File("some_file_location"));
m.addFormField("value_name","value");
//call either of the below methods based on what you need; do not call both!
String result=m.finish();//call this if the response is a small text
InputStream is=m.finish_with_inputstream(); //call this if the response is huge and not fitting in memory; don't forget to disconnect the connection afterwards;
} catch (IOException e) {
e.printStackTrace();
}
now you can handle all these data in one request on the php side:
$value=$_POST["value_name"];
$file_data=file_get_contents($_FILES["file_part"]["tmp_name"])
so for you situation it would be like below:
try{
MultipartUtility m=new MultipartUtility("https://mydomain");
m.addFilePart("bill",new File(sourceFile));
m.addFormField(URLEncoder.encode("username" , "UTF-8"),URLEncoder.encode(username,"UTF-8"));
String result=m.finish();
} catch (Exception e) {
e.printStackTrace();
}
using the below code, i am able to send a large file to the server, but i cant seem to find out how send a text with the file.(send the file plus extra data(username, password..) for example). and also receive it at the server side.
FileInputStream fileInputStream = new FileInputStream(new File(
pathToOurFile));
URL url = new URL(urlServer);
connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setChunkedStreamingMode(1024);
// Enable POST method
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type",
"multipart/form-data;boundary=" + boundary);
outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.writeBytes(twoHyphens + boundary + lineEnd);
String connstr = null;
connstr = "Content-Disposition: form-data; name=\"uploadedVideos\";filename=\""
+ pathToOurFile + "\"" + lineEnd;
outputStream.writeBytes(connstr);
outputStream.writeBytes(lineEnd);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// Read file
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
try {
while (bytesRead > 0) {
try {
outputStream.write(buffer, 0, bufferSize);
} catch (OutOfMemoryError e) {
e.printStackTrace();
response = "outofmemoryerror";
return response;
}
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
} catch (Exception e) {
e.printStackTrace();
response = "error";
return response;
}
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(twoHyphens + boundary + twoHyphens
+ lineEnd);
fileInputStream.close();
outputStream.flush();
outputStream.close();
the server part:
HttpPostedFile file = HttpContext.Current.Request.Files[0];
string fname = Path.GetFileName(file.FileName);
file.SaveAs(Server.MapPath(Path.Combine("~/UploadedVideos/" + Date + "/", fname)));
please any help will be appreciated.
i know i can do this using HttpClient, but its not working for me in case of large files so i want to use this way.
Use this,
private static String multipost(String urlString, MultipartEntity reqEntity) {
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("Connection", "Keep-Alive");
conn.addRequestProperty("Content-length", reqEntity.getContentLength()+"");
conn.addRequestProperty(reqEntity.getContentType().getName(), reqEntity.getContentType().getValue());
OutputStream os = conn.getOutputStream();
reqEntity.writeTo(conn.getOutputStream());
os.close();
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
return readStream(conn.getInputStream());
}
} catch (Exception e) {
Log.e("MainActivity", "multipart post error " + e + "(" + urlString + ")");
}
return null;
}
private static String readStream(InputStream in) {
BufferedReader reader = null;
StringBuilder builder = new StringBuilder();
try {
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
builder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return builder.toString();
}
and here you can set File, username and password as like below using MultipartEntiry.
private void uploadMultipartData() throws UnsupportedEncodingException {
MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
reqEntity.addPart("fileToUpload", new FileBody(uploadFile));
reqEntity.addPart("uname", new StringBody("MyUserName"));
reqEntity.addPart("pwd", new StringBody("MyPassword"));
String response = multipost(server_url, reqEntity);
Log.d("MainActivity", "Response :"+response);
}
Note: You need to add httpmime jar in your libs folder.
this is my working code, i recalled copying this from somewhere, cant recall it...
package my.com.myapp.utils;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
* This utility class provides an abstraction layer for sending multipart HTTP
* POST requests to a web server.
* #author www.codejava.net
*
*/
public class MultipartUtil {
private final String boundary;
private static final String LINE_FEED = "\r\n";
private HttpURLConnection httpConn;
protected String charset;
protected OutputStream outputStream;
protected PrintWriter writer;
public boolean cancel = false;
protected boolean last_item_is_file = false;
public static String LOG_TAG = "MultipartUtil";
/**
* This constructor initializes a new HTTP POST request with content type
* is set to multipart/form-data
* #param requestURL
* #param charset
* #throws IOException
*/
public MultipartUtil(String requestURL, String charset, Boolean send_auth)
throws IOException {
this.charset = charset;
// creates a unique boundary based on time stamp
boundary = "===" + System.currentTimeMillis() + "===";
URL url = new URL(requestURL);
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setUseCaches(false);
httpConn.setDoOutput(true); // indicates POST method
httpConn.setDoInput(true);
httpConn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + boundary);
httpConn.setRequestProperty("User-Agent", "Android submit");
//addHeaderField("lang", AppConfig.getInstance().language);
//addHeaderField("country", AppConfig.getInstance().country);
//User user = AppConfig.getInstance().get_user();
/*
if (send_auth && user != null && user.authentication_token != null && !user.authentication_token.equalsIgnoreCase("")) {
Log.i(LOG_TAG, "user token: "+user.authentication_token);
addHeaderField("X-User-Email", user.email);
addHeaderField("X-User-Token", user.authentication_token);
} else {
Log.i(LOG_TAG, "not auth / not logged in");
}
*/
// httpConn.setRequestProperty("Test", "Bonjour");
}
public PrintWriter setup_writer() throws IOException {
outputStream = httpConn.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, charset),
true);
return writer;
}
/**
* Adds a form field to the request
* #param name field name
* #param value field value
*/
public void addFormField(String name, String value) {
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"" + name + "\"")
.append(LINE_FEED);
writer.append("Content-Type: text/plain; charset=" + charset).append(
LINE_FEED);
writer.append(LINE_FEED);
writer.append(value).append(LINE_FEED);
writer.flush();
last_item_is_file = false;
}
/**
* Adds a upload file section to the request
* #param fieldName name attribute in <input type="file" name="..." />
* #param uploadFile a File to be uploaded
* #throws IOException
*/
public void addFilePart(String fieldName, File uploadFile)
throws IOException {
String fileName = uploadFile.getName();
writer.append("--" + boundary).append(LINE_FEED);
writer.append(
"Content-Disposition: form-data; name=\"" + fieldName
+ "\"; filename=\"" + fileName + "\"")
.append(LINE_FEED);
writer.append(
"Content-Type: "
+ URLConnection.guessContentTypeFromName(fileName))
.append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
FileInputStream inputStream = new FileInputStream(uploadFile);
byte[] buffer = new byte[4096];
int bytesRead = -1;
while (!cancel && (bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
inputStream.close();
if (cancel) {
writer.close();
return;
}
writer.append(LINE_FEED);
writer.flush();
last_item_is_file = true;
}
public void addFileStreamPart(String fieldName, String fileName, InputStream inputStream)
throws IOException {
writer.append("--" + boundary).append(LINE_FEED);
writer.append(
"Content-Disposition: form-data; name=\"" + fieldName
+ "\"; filename=\"" + fileName + "\"")
.append(LINE_FEED);
writer.append(
"Content-Type: "
+ URLConnection.guessContentTypeFromName(fileName))
.append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
byte[] buffer = new byte[4096];
int bytesRead = -1;
while (!cancel && (bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
inputStream.close();
if (cancel) {
writer.close();
return;
}
writer.append(LINE_FEED);
writer.flush();
last_item_is_file = true;
}
/**
* Adds a header field to the request.
* #param name - name of the header field
* #param value - value of the header field
*/
public void addHeaderField(String name, String value) {
httpConn.setRequestProperty(name, value);
//writer.append(name + ": " + value).append(LINE_FEED).flush();
//addFormField("headers["+name+"]", value);
//writer.append(name + ": " + value).append(LINE_FEED).flush();
}
public HttpURLConnection finish() throws IOException {
if (last_item_is_file)
writer.append(LINE_FEED).flush();
writer.append("--" + boundary + "--").append(LINE_FEED);
writer.close();
return httpConn;
}
}
I would like upload any data (Username, Description,..) and a photo from my Android-App to my Server (PHP-Script).
By searching for this i found a solution: How to take a photo and send to HTTP POST request with Android?
By click on my send button, the upload-process starts and the php-script works but the $_POST -Vars are empty? Can anybody help me? My request-code:
[...]
case R.id.btnSend:
String strName = tName.getText().toString();
String strEmail = tEmail.getText().toString();
String strDatum = tDatum.getText().toString();
String strBeschreibung = tBeschreibung.getText().toString();
nvPairs = new ArrayList<NameValuePair>(2);
nvPairs.add(new BasicNameValuePair("sender", "AndoidApp"));
nvPairs.add(new BasicNameValuePair("name", strName));
nvPairs.add(new BasicNameValuePair("email", strEmail));
nvPairs.add(new BasicNameValuePair("datum", strDatum));
nvPairs.add(new BasicNameValuePair("beschreibung", strBeschreibung));
nvPairs.add(new BasicNameValuePair("bild", bmpUrl));
new UploadTask().execute();
}
[...]
private class UploadTask extends AsyncTask<Void, Void, List<String>> {
#Override
protected List<String> doInBackground(Void... params) {
String response = "";
try {
response = new MultipartServer().postData(new URL(postUrl), nvPairs);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
On the MultipartServer-Code i changed the line:
if ((avatarName = pair.getName()).equals("avatar")) {
avatarPath = pair.getValue();
}
to this:
if ((avatarName = pair.getName()).equals("bild")) {
avatarPath = pair.getValue();
}
And the Variables "bmpUrl" and "postUrl" are set correctly at an another line in my code.
Thanks for your help! I found an solution for the problem. The problem was at the Script from How to take a photo and send to HTTP POST request with Android?
I changed it to this:
public class MultipartServer {
private static final String TAG = "MultipartServer";
private static final String crlf = "\r\n";
private static final String twoHyphens = "--";
private static final String boundary = "AaB03x"; //*****
private String avatarName = null;
private String avatarPath = null;
public String postData(URL url, List<NameValuePair> nameValuePairs) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(10000);
connection.setConnectTimeout(15000);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Cache-Control", "no-cache");
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
FileInputStream inputStream;
OutputStream outputStream = connection.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
for (NameValuePair pair : nameValuePairs) {
dataOutputStream.writeBytes(crlf);
dataOutputStream.writeBytes(twoHyphens + boundary + crlf);
dataOutputStream.writeBytes(
"Content-Disposition: form-data; name=\""
+ URLEncoder.encode(pair.getName(), "UTF-8")
+ "\";" + crlf);
dataOutputStream.writeBytes(crlf);
dataOutputStream.writeBytes(URLEncoder.encode(pair.getValue(), "UTF-8"));
if (pair.getName().equals("bild")) {
avatarName = pair.getName();
avatarPath = pair.getValue();
}
}
// Write Avatar (if any)
if (avatarName != null && avatarPath != null) {
dataOutputStream.writeBytes(crlf);
dataOutputStream.writeBytes(twoHyphens + boundary + crlf);
dataOutputStream
.writeBytes("Content-Disposition: form-data; name=\""
+ avatarName + "\";filename=\""
+ new File(avatarPath).getName() + "\";" + crlf);
dataOutputStream.writeBytes(crlf);
inputStream = new FileInputStream(avatarPath);
byte[] data = new byte[1024];
int read;
while ((read = inputStream.read(data)) != -1)
dataOutputStream.write(data, 0, read);
inputStream.close();
dataOutputStream.writeBytes(crlf);
dataOutputStream.writeBytes(twoHyphens + boundary + twoHyphens + crlf);
}
dataOutputStream.flush();
dataOutputStream.close();
String responseMessage = connection.getResponseMessage();
Log.d(TAG, responseMessage);
InputStream in = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(in, "UTF-8"));
StringBuilder response = new StringBuilder();
char[] b = new char[512];
int read;
while ((read = bufferedReader.read(b)) != -1) {
response.append(b, 0, read);
}
connection.disconnect();
Log.d(TAG, response.toString());
return response.toString();
}
}
I am new to PHP. I am building an android project which needs to upload images to my server. The problem i am having is that when I send just a key and a value (no file) to the server, it works perfectly fine. However, as soon as I try to send a file, the superglobals $_POST and $_FILES in php are empty! The file sent is very small, so its not go to do with the file_max_upload_size. The file is not corrupted. I think it is something to do with the encoding of of the InputStream sent by the app on the android emulator. My code is below:
Java code in the app that sends the image along with a key-value pair:
public Future<JSONObject> asyncSendPOSTRequest(String URL, Map<String, String> params, Map<String, Pair<String,InputStream>> files) throws InterruptedException, ExecutionException, JSONException, UnsupportedEncodingException {
HttpPost request = new HttpPost(URL);
MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create();
if(params!=null) {
for(String key : params.keySet()) {
multipartEntity.addTextBody(key, params.get(key), ContentType.TEXT_PLAIN);
}
}
if(files!=null) {
for(String key : files.keySet()) {
multipartEntity.addPart(key, new InputStreamBody(files.get(key).second,ContentType.MULTIPART_FORM_DATA, files.get(key).first));
}
}
request.setEntity(multipartEntity.build());
Future<JSONObject> future = threadPool.submit(new executeRequest(request));
return future;
}
//Thread to communicate with server.
private class executeRequest implements Callable<JSONObject> {
HttpRequestBase request;
public executeRequest(HttpRequestBase request) {
this.request = request;
}
#Override
public JSONObject call() throws Exception {
HttpResponse httpResponse = httpClient.execute(request);
BufferedReader reader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
StringBuilder stringReply = new StringBuilder();
String replyLine;
while ((replyLine = reader.readLine()) != null) {
stringReply.append(replyLine);
}
return new JSONObject(stringReply.toString());
}
}
The code on the server:
#!/usr/bin/php
<?php
$uploads_dir = __DIR__ . '/uploads';
$status = -1;
if ($_FILES["picture"]["error"] == UPLOAD_ERR_OK) {
$tmp_name = $_FILES["picture"]["tmp_name"];
$name = $_FILES["picture"]["name"];
$status = move_uploaded_file($tmp_name, "$uploads_dir/$name");
}
$response["status"] = $status;
$response["user_id"] = $_POST["user_id"];
$response["name"] = $name;
$response["extension"] = end (explode(".", $name));
echo json_encode($response);
?>
The possible problem that you set wrong datatype
enctype="multipart/form-data"
public class Helpher extends AsyncTask<String, Void, String> {
Context context;
JSONObject json;
ProgressDialog dialog;
int serverResponseCode = 0;
DataOutputStream dos = null;
FileInputStream fis = null;
BufferedReader br = null;
public Helpher(Context context) {
this.context = context;
}
protected void onPreExecute() {
dialog = ProgressDialog.show(Main2Activity.this, "ProgressDialog", "Wait!");
}
#Override
protected String doInBackground(String... arg0) {
try {
File f = new File(arg0[0]);
URL url = new URL("http://localhost:8888/imageupload.php");
int bytesRead;
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
String contentDisposition = "Content-Disposition: form-data; name=\"keyValueForFile\"; filename=\""
+ f.getName() + "\"";
String contentType = "Content-Type: application/octet-stream";
dos = new DataOutputStream(conn.getOutputStream());
fis = new FileInputStream(f);
dos.writeBytes(SPACER + BOUNDARY + NEW_LINE);
dos.writeBytes("Content-Disposition: form-data; name=\"parameterKey\""
+ NEW_LINE);
dos.writeBytes(NEW_LINE);
dos.writeBytes("parameterValue" + NEW_LINE);
dos.writeBytes(SPACER + BOUNDARY + NEW_LINE);
dos.writeBytes(contentDisposition + NEW_LINE);
dos.writeBytes(contentType + NEW_LINE);
dos.writeBytes(NEW_LINE);
byte[] buffer = new byte[MAX_BUFFER_SIZE];
while ((bytesRead = fis.read(buffer)) != -1) {
dos.write(buffer, 0, bytesRead);
}
dos.writeBytes(NEW_LINE);
dos.writeBytes(SPACER + BOUNDARY + SPACER);
dos.flush();
int responseCode = conn.getResponseCode();
if (responseCode != 200) {
Log.w(TAG,
responseCode + " Error: " + conn.getResponseMessage());
return null;
}
br = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
Log.d(TAG, "Sucessfully uploaded " + f.getName());
} catch (MalformedURLException e) {
} catch (IOException e) {
} finally {
try {
dos.close();
if (fis != null)
fis.close();
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return String.valueOf(serverResponseCode);
}
#Override
protected void onPostExecute(String result) {
dialog.dismiss();
}
}
This is the AsyncTask "Helpher" class used for upload image from Android. To call this class use like syntax below.
new Main2Activity.Helpher(this).execute(fileUri.getPath(),parameterValue);
Here fileUri.getPath() local image location.If you want to see the server response value is avilable in " StringBuilder sb" you can print sb value
I am trying to post a JSON representation of my object and an optional image to my Spring backend. However, I am having major trouble posting a request with an image. I have managed to post the first part to the server; the JSON object. However when I try to add a second part which is an image captured by the user nothing happens. I don't see any stacktraces.
What am I doing wrong and how do I post an image along the request. This is what I have...
String boundary = UUID.randomUUID().toString();
String twoHyphens = "--";
String attachmentName = "data";
String crlf = "\r\n";
try {
URI uri = new URI("http://myappserver.com/app");
HttpURLConnection urlConnection = (HttpURLConnection) uri.toURL().openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Connection", "Keep-Alive");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
DataOutputStream dos = new DataOutputStream(urlConnection.getOutputStream());
InputStream inputStream = context.getContentResolver().openInputStream(question.getUri());
// FIRST PART; A JSON object
dos.writeBytes(twoHyphens + boundary);
dos.writeBytes(crlf);
dos.writeBytes("Content-Type: application/json");
dos.writeBytes(crlf);
dos.writeBytes("Content-Disposition: form-data; name=\"data\"");
dos.writeBytes(crlf);
dos.writeBytes(crlf);
dos.writeBytes(jsonEntity);
dos.writeBytes(crlf);
// SECOND PART; A image..
dos.writeBytes(twoHyphens + boundary);
dos.writeBytes(crlf);
dos.writeBytes("Content-Type: image/jpg");
dos.writeBytes(crlf);
dos.writeBytes("Content-Disposition: form-data; name=\"image\"");
dos.writeBytes(crlf);
dos.writeBytes(crlf);
// Something must be done here. I guess I must encode it to Base64 here.
// How can I avoid loading the whole image at once so I don't get out of memory errors.
dos.writeBytes(crlf);
dos.writeBytes(twoHyphens + boundary + twoHyphens + crlf);
dos.close();
} catch (Exception e) {
}
The code illustrates how to send a file (here it is pdf). Go through it, it will be helpful for you to debug your code.
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 1 * 1024 * 1024;
FileInputStream fileInputStream = new FileInputStream(file);
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"attachment_0\";filename=\"" + file.getName() + "\"" + lineEnd);
dos.writeBytes("Content-Type: text/pdf" + lineEnd);
dos.writeBytes("Content-Length: " + file.length() + lineEnd);
dos.writeBytes(lineEnd);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0)
{
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + lineEnd);
fileInputStream.close();
}
//for sending a parameter, see below, key is the key of the webservice parameter.
dos.writeBytes("Content-Disposition: form-data; name=\""+key+"\"" + lineEnd);
dos.writeBytes(lineEnd);
dos.writeBytes(requestData.get(key).toString() + lineEnd);
Below is my implementation of a MULTIPART form data upload using HttpURLConnection class. This class employs a Dynamic usage so that multiple request can be invoked at a time with different sets of data.
NB: I'm breaking the json part into individual text forms inorder to maintain consistency with my server. You may modify it to match your servers requirement.
public class WebConnector {
String boundary = "-------------" + System.currentTimeMillis();
private static final String LINE_FEED = "\r\n";
private static final String TWO_HYPHENS = "--";
private StringBuilder url;
private String protocol;
private HashMap<String, String> params;
private JSONObject postData;
private List<String> fileList;
private int count = 0;
private DataOutputStream dos;
public WebConnector(StringBuilder url, String protocol,
HashMap<String, String> params, JSONObject postData) {
super();
this.url = url;
this.protocol = protocol;
this.params = params;
this.postData = postData;
createServiceUrl();
}
public WebConnector(StringBuilder url, String protocol,
HashMap<String, String> params, JSONObject postData, List<String> fileList) {
super();
this.url = url;
this.protocol = protocol;
this.params = params;
this.postData = postData;
this.fileList = fileList;
createServiceUrl();
}
public String connectToMULTIPART_POST_service(String postName) {
System.out.println(">>>>>>>>>url : " + url);
StringBuilder stringBuilder = new StringBuilder();
String strResponse = "";
InputStream inputStream = null;
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) new URL(url.toString()).openConnection();
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestProperty("Connection", "close");
urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 ( compatible ) ");
urlConnection.setRequestProperty("Authorization", "Bearer " + Config.getConfigInstance().getAccessToken());
urlConnection.setRequestProperty("Content-type", "multipart/form-data; boundary=" + boundary);
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setUseCaches(false);
urlConnection.setChunkedStreamingMode(1024);
urlConnection.setRequestMethod("POST");
dos = new DataOutputStream(urlConnection.getOutputStream());
Iterator<String> keys = postData.keys();
while (keys.hasNext()) {
try {
String id = String.valueOf(keys.next());
addFormField(id, "" + postData.get(id));
System.out.println(id + " : " + postData.get(id));
} catch (JSONException e) {
e.printStackTrace();
}
}
try {
dos.writeBytes(LINE_FEED);
dos.flush();
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
if (fileList != null && fileList.size() > 0 && !fileList.isEmpty()) {
for (int i = 0; i < fileList.size(); i++) {
File file = new File(fileList.get(i));
if (file != null) ;
addFilePart("photos[" + i + "][image]", file);
}
}
// forming th java.net.URL object
build();
urlConnection.connect();
int statusCode = 0;
try {
urlConnection.connect();
statusCode = urlConnection.getResponseCode();
} catch (EOFException e1) {
if (count < 5) {
urlConnection.disconnect();
count++;
String temp = connectToMULTIPART_POST_service(postName);
if (temp != null && !temp.equals("")) {
return temp;
}
}
} catch (IOException e) {
e.printStackTrace();
}
// 200 represents HTTP OK
if (statusCode == HttpURLConnection.HTTP_OK) {
inputStream = new BufferedInputStream(urlConnection.getInputStream());
strResponse = readStream(inputStream);
} else {
System.out.println(urlConnection.getResponseMessage());
inputStream = new BufferedInputStream(urlConnection.getInputStream());
strResponse = readStream(inputStream);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != inputStream)
inputStream.close();
} catch (IOException e) {
}
}
return strResponse;
}
public void addFormField(String fieldName, String value) {
try {
dos.writeBytes(TWO_HYPHENS + boundary + LINE_FEED);
dos.writeBytes("Content-Disposition: form-data; name=\"" + fieldName + "\"" + LINE_FEED + LINE_FEED/*+ value + LINE_FEED*/);
/*dos.writeBytes("Content-Type: text/plain; charset=UTF-8" + LINE_FEED);*/
dos.writeBytes(value + LINE_FEED);
} catch (IOException e) {
e.printStackTrace();
}
}
public void addFilePart(String fieldName, File uploadFile) {
try {
dos.writeBytes(TWO_HYPHENS + boundary + LINE_FEED);
dos.writeBytes("Content-Disposition: form-data; name=\"" + fieldName + "\";filename=\"" + uploadFile.getName() + "\"" + LINE_FEED);
dos.writeBytes(LINE_FEED);
FileInputStream fStream = new FileInputStream(uploadFile);
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int length = -1;
while ((length = fStream.read(buffer)) != -1) {
dos.write(buffer, 0, length);
}
dos.writeBytes(LINE_FEED);
dos.writeBytes(TWO_HYPHENS + boundary + TWO_HYPHENS + LINE_FEED);
/* close streams */
fStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void addHeaderField(String name, String value) {
try {
dos.writeBytes(name + ": " + value + LINE_FEED);
} catch (IOException e) {
e.printStackTrace();
}
}
public void build() {
try {
dos.writeBytes(LINE_FEED);
dos.flush();
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String readStream(InputStream in) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String nextLine = "";
while ((nextLine = reader.readLine()) != null) {
sb.append(nextLine);
}
/* Close Stream */
if (null != in) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
private void createServiceUrl() {
if (null == params) {
return;
}
final Iterator<Map.Entry<String, String>> it = params.entrySet().iterator();
boolean isParam = false;
while (it.hasNext()) {
final Map.Entry<String, String> mapEnt = (Map.Entry<String, String>) it.next();
url.append(mapEnt.getKey());
url.append("=");
try {
url.append(URLEncoder.encode(mapEnt.getValue(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
url.append(WSConstants.AMPERSAND);
isParam = true;
}
if (isParam) {
url.deleteCharAt(url.length() - 1);
}
}
}