Google Drive file upload in hybrid apps - android

We have a hybrid app where most of the code is handled in Javascript (including logging in via Google Sign In and some uploading to Google Drive). On iOS, we have other Google Drive uploading code, but I can't figure out how to accomplish the same thing in Android. I'm trying avoid having the user log into the web portion and then again for Android.
The iOS code that is being used to upload is...
let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart")!)
mutableURLRequest.HTTPMethod = "POST"
let boundaryConstant = generateBoundaryString()
let contentType = "multipart/related; boundary="+boundaryConstant
// Set the headers
mutableURLRequest.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
mutableURLRequest.addValue("Keep-Alive", forHTTPHeaderField: "Connection")
mutableURLRequest.addValue(contentType, forHTTPHeaderField: "Content-Type")
mutableURLRequest.addValue("\(requestData.length)", forHTTPHeaderField: "Content-Length")
// create upload data to send
let uploadData = NSMutableData()
uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Day, .Month, .Year], fromDate: date)
let fileName = "\(components.year)-\(components.month)-\(components.day).\(ext)"
// Add parameters
let params = [
"name": fileName,
"mimeType": mimeType,
"parents": ["\(slnFldrId)"],
"convert": true
]
// Add the file meta data (JSON format)
uploadData.appendData("Content-Type: application/json; charset=UTF-8\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
uploadData.appendData("\(getJsonString(params))\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
// Add the file
uploadData.appendData("Content-Type: \(mimeType)\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
uploadData.appendData(requestData)
uploadData.appendData("\r\n--\(boundaryConstant)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
mutableURLRequest.HTTPBody = uploadData
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
let task = session.uploadTaskWithStreamedRequest(mutableURLRequest)
task.resume()
...So it's just using the REST endpoint and works nicely. For Android, I've tried using the HttpClient with HttpPost and setting headers, etc., but I always get a 400 error (Bad request).
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
String mimeType = mimeTypeFromTypeId(typeId);
String boundary = getBoundary();
String tail = "\r\n-"+ boundary +"--\r\n";
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 30000);
HttpConnectionParams.setSoTimeout(httpParams, 30000);
HttpClient httpClient = new DefaultHttpClient(httpParams);
HttpPost httpPost = new HttpPost("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart");
httpPost.addHeader("Authorization", "Bearer "+ Common.accessToken);
httpPost.addHeader("Content-Length", ""+ file.length());
String json = "{\"name\":\"Android upload."+ ext +"\",\"mimeType\":\""+ mimeType +"\",\"parents\":[\""+ Common.slnFldrId +"\"],\"convert\":true}";
MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
entityBuilder.setBoundary(boundary);
entityBuilder.setCharset(MIME.UTF8_CHARSET);
entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
entityBuilder.addTextBody("", json, ContentType.APPLICATION_JSON);
entityBuilder.addBinaryBody("", file, ContentType.create(mimeType), "Android upload."+ ext);
httpPost.setEntity(entityBuilder.build());
httpPost.setHeader(HTTP.CONTENT_TYPE, "multipart-related; boundary="+ boundary);
try
{
return httpClient.execute(httpPost);
}
catch(ConnectTimeoutException e)
{
throw e;
}
...I think the headers are causing the bad request error. Even though I'm setting the Content-Type header to multipart/related, it is always converted to multipart/form-data (I think by the MultipartEntityBuilder).
As far as I can tell, the Google Client libraries all require an authorization process (in that I can't set the accessToken that I already have from the web login into them), which is why I'm not using them.
Any help or suggestions would be greatly appreciated.

The solution I found is based off the article at http://www.codejava.net/java-se/networking/upload-files-by-sending-multipart-request-programmatically
Here is the code with some changes to make it work correctly with the Google Drive API.
URL url = null;
HttpURLConnection connection = null;
try
{
// Set up some constants and "global" variables
String BOUNDARY = getBoundary();
String LINE_FEED = "\r\n";
String UTF8 = "UTF-8";
String fileName = file.getName();
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
String mimeType = mimeTypeFromTypeId(typeId);
// Use the calendar to give the file a name
Calendar cal = Calendar.getInstance();
int cYear = cal.get(Calendar.YEAR);//calender year starts from 1900 so you must add 1900 to the value recevie.i.e., 1990+112 = 2012
int cMonth = cal.get(Calendar.MONTH);//this is april so you will receive 3 instead of 4.
int cDay = cal.get(Calendar.DAY_OF_MONTH);
int cHour = cal.get(Calendar.HOUR_OF_DAY);
int cMin = cal.get(Calendar.MINUTE);
int cSec = cal.get(Calendar.SECOND);
String name = cYear +"-"+ cMonth +"-"+ cDay +"-"+ cHour +"-"+ cMin +"-"+ cSec +"."+ ext;
// JSON meta-data part
String json = "{\"name\":\""+ name +"\",\"mimeType\":\""+ mimeType +"\",\"parents\":[\""+ Common.slnFldrId +"\"],\"convert\":true}";
String META_PART1 = "--"+ BOUNDARY + LINE_FEED
+ "Content-Type: application/json; charset="+ UTF8 + LINE_FEED + LINE_FEED
+ json + LINE_FEED + LINE_FEED;
// File meta-data part
String META_PART2 = "--"+ BOUNDARY + LINE_FEED
+ "Content-Type: "+ mimeType + LINE_FEED + LINE_FEED;
// Tail
String TAIL = LINE_FEED +"--"+ BOUNDARY +"--";
long contentLength = META_PART1.length() + META_PART2.length() + file.length() + TAIL.length();
// Set up the HttpUrlConnection
url = new URL("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart");
connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(10000);
connection.setConnectTimeout(15000);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setInstanceFollowRedirects(false);
connection.setRequestProperty("Authorization", "Bearer "+ Common.accessToken);
connection.setRequestProperty("Content-Type", "multipart/related; boundary="+ BOUNDARY);
connection.setRequestProperty("Content-Length", ""+ contentLength);
connection.connect();
// Get the connection's output stream
OutputStream outputStream = connection.getOutputStream();
// Write the META_PART1 (JSON) and flush
outputStream.write(META_PART1.getBytes(UTF8));
outputStream.flush();
// Write the META_PART2 (file's contentType) and flush
outputStream.write(META_PART2.getBytes(UTF8));
outputStream.flush();
// Write the FILE
int totalRead = 0;
int bytesRead = -1;
byte[] buffer = new byte[4096];
FileInputStream inputStream = new FileInputStream(file);
while ((bytesRead = inputStream.read(buffer)) != -1)
{
outputStream.write(buffer, 0, bytesRead);
outputStream.flush();
totalRead += bytesRead;
float progress = totalRead / (float)contentLength;
delegate.onUploadProgress(progress);
}
// Flush the last of the file data and close the inputStream
outputStream.flush();
inputStream.close();
// Flush the TAIL and close the outputStream
outputStream.write(TAIL.getBytes(UTF8));
outputStream.flush();
outputStream.close();
// Get the server response
int responseCode = connection.getResponseCode();
if(responseCode == HTTP_SUCCESS)
{
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = "";
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null)
{
builder.append(line);
}
// Send the completed message to the delegate
delegate.onUploadComplete(builder.toString(), typeId);
}
else
{
// Send the error message to the delegate
delegate.onUploadError(file);
}
}catch(IOException e)
{
// Send the error message to the delegate
delegate.onUploadError(file);
}finally
{
if(connection != null)
{
connection.disconnect();
}
}

Related

Android - Best practices for uploading images to the server

I have some code that sends string bytes to the server then in mvc c# i take these string bytes convert it into raw bytes and then save the bytes to file. This works great but the problem when i am sending 4 images of string bytes inside of HttpURLConnection the mvc server comes back with saying File Not Found Exception.
So i found out when i sent large images it would fail with this exception, and when i sent images lower that 3080000 bytes it sent the images and saved them to the server.
So now im deciding to show a message to the user displaying a dialog box about the file limit, is this good to do?
This is my code:
Client
try {
if (imgS!=null && imgS.size()>0) {
for (int i = 0; i < imgS.size(); i++) {
if(i == 0){
image1 = Base64.encodeToString(imgS.get(i).bytes, Base64.DEFAULT);
}
if(i == 1){
image2 = Base64.encodeToString(imgS.get(i).bytes, Base64.DEFAULT);
}
if(i == 2){
image3 = Base64.encodeToString(imgS.get(i).bytes, Base64.DEFAULT);
}
if(i == 3){
image4 = Base64.encodeToString(imgS.get(i).bytes, Base64.DEFAULT);
}
}
}
if (!isDelivered) {
deliveredId = 2;
}
if (params[0] != null && (params[0].equals("0.00") || !params[0].equals(""))) {
priceTmp = Double.valueOf(params[0]);
}
if (params[6] != null && (params[6].equals("0.00") || !params[6].equals(""))) {
postageTmp = Double.valueOf(params[6]);
}
String responseText = "";
SharedPreferences preferences = getActivity().getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE);
String json = String.format("{\"p_u_id\": \"%s\",\"price\": \"%.2f\"," +
"\"title\": \"%s\"" +
",\"description\": \"%s\",\"category\": \"%d\",\"tags\": \"%s\"" +
",\"image1\": \"%s\",\"image2\": \"%s\",\"image3\": \"%s\",\"image4\": \"%s\"" +
",\"postcode\": \"%s\",\"postage\": \"%.2f\",\"isDelivered\": \"%d\"}",
preferences.getString("userId", "0"),
priceTmp,
params[1],
params[2],
selectedCategory,
params[4],
image1,
image2,
"",
"",
params[5],
postageTmp,
deliveredId
);
long b = image1.getBytes().length + image2.getBytes().length +image3.getBytes().length + image4.getBytes().length;
if(b > 3080000){
return "FileLimit";
}else{
URL url = new URL("http://xxx/Home/xxxx");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(10000);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json"); // have tried without this
conn.setRequestProperty("Content-Length", json.getBytes().length + "");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(json.getBytes("UTF-8"));
os.close();
conn.connect();
InputStream is = conn.getInputStream();
//here we read and print the response send by server; assuming that response type is text/html (MIME Type)
StringBuilder sb = new StringBuilder();
int ch;
//-1: end of stream
while ((ch = is.read()) != -1) {
sb.append((char) ch);
}
responseText = sb.toString();
conn.disconnect();
JSONObject obj = new JSONObject(responseText);
pid = obj.getInt("id");
serverImage1 = obj.getString("image1");
serverImage2 = obj.getString("image2");
json = String.format("{\"p_id\": \"%d\",\"image1\": \"%s\"," +
"\"image2\": \"%s\",\"p_u_id\": \"%s\"}",
pid,
image3,
image4,
preferences.getString("userId", "0")
);
url = new URL("http://xxx/Home/xxxx");
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(10000);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json"); // have tried without this
conn.setRequestProperty("Content-Length", json.getBytes().length + "");
conn.setDoOutput(true);
os = conn.getOutputStream();
os.write(json.getBytes("UTF-8"));
os.close();
conn.connect();
is = conn.getInputStream();
//here we read and print the response send by server; assuming that response type is text/html (MIME Type)
sb = new StringBuilder();
ch = 0;
//-1: end of stream
while ((ch = is.read()) != -1) {
sb.append((char) ch);
}
responseText = sb.toString();
return responseText;
}
} catch (Exception e) {
int i = 0;
}
What i have done here is send the first two images to the server then send the next two images after the first two images were sent. This was funny because still if i send images two big the server will come back with an exception again.
So is it good to show a message to the user about file limit?
And how do other apps like shpock n Ebay work when they allow mulitple file uploads?
I tried using some Android Libraries but was unsusseccful.
Would like to know a way of sending maxiumum of four files without showing the user a file limit and just send the 4 images directly.
MVC
if(image.Length > 0)
{
byte[] bytes = Convert.FromBase64String(image);
if (!System.IO.Directory.Exists(Server.MapPath("~/App_Data/products/" + uid)))
{
System.IO.Directory.CreateDirectory(Server.MapPath("~/App_Data/products/") + uid);
}
if (!System.IO.Directory.Exists(Server.MapPath("~/App_Data/products/" + uid + "/" + pid)))
{
System.IO.Directory.CreateDirectory(Server.MapPath("~/App_Data/products/") + uid + "/" + pid);
}
string path = Path.Combine(Server.MapPath("~/App_Data/products/" + uid + "/" + pid), guid + "" + ".jpg");
System.IO.File.WriteAllBytes(path, bytes);
}
When you have a Image you can upload it to a Cloud Database like Firestore or Amazon S3 or whatever and then you send the link instead of the bytes!
Is it a good idea to show file limit to user? Yes it is. Better than errors popping up for them.
Why is it not working for larger than 3MB images? Well, if you look at the Microsoft docs, your Convert.FromBase64String method puts the data into an unsigned 8bit integer array. An unsigned int maxes out at 4.29 Million, so because you have base64 encoded, your image is actually 33% bigger, because it has a ratio of 4:3 when you base64 encode.
Convert.FromBase64String(String) Method
Base64 Padding file size increases
Thus, you cant upload anything bigger than ~3MB.
How do other companies do multi file upload? Well they just call the same file upload endpoint, asynchronously for each file. So if you send 4 files, it will make 4 requests at the same time, and these requests will slowly upload the image (you can usually see each files individual progress bar indicator).

Android sending image to Server via MultipartEntity - setting Content-type?

I've read many many posts about sending an image to the server from an Android app and Content-type-wise, they are divided in three categories:
a) they dont set the content-type at all and probably somehow their code works
b) they are using deprecated methods
c) they are using completely different approach from the one I've selected.
I would like to send the file over to the server and store it in a folder.
My code is a total patchwork, a butchery job that I managed to come up with after reading lots of posts and articles, here is it:
public void uploadImageToServer(String imagePath) throws Exception {
try {
// set the http handlers
httpClient = new DefaultHttpClient();
localContext = new BasicHttpContext(); // why do I need this?
postRequest = new HttpPost("http://asd.com/asd.php");
//postRequest.addHeader("Content-type", "image/jpeg"); // - this didnt work
// deal with the file
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap = BitmapFactory.decodeFile(imagePath);
bitmap.compress(CompressFormat.JPEG, 75, byteArrayOutputStream);
byte[] byteData = byteArrayOutputStream.toByteArray();
//String strData = Base64.encodeToString(data, Base64.DEFAULT); // I have no idea why Im doing this
ByteArrayBody byteArrayBody = new ByteArrayBody(byteData, "image"); // second parameter is the name of the image (//TODO HOW DO I MAKE IT USE THE IMAGE FILENAME?)
// send the package
multipartEntity = MultipartEntityBuilder.create();
multipartEntity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
multipartEntity.addPart("uploaded_file", byteArrayBody);
postRequest.setEntity(multipartEntity.build());
// get the response. we will deal with it in onPostExecute.
response = httpClient.execute(postRequest, localContext);
bitmap.recycle();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
And the error is:
FATAL EXCEPTION: AsyncTask #1
java.lang.RuntimeException: An error occured while executing doInBackground()
android.os.AsyncTask$3.done(AsyncTask.java:200)
java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
java.util.concurrent.FutureTask.setException(FutureTask.java:125)
java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
java.util.concurrent.FutureTask.run(FutureTask.java:138)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
java.lang.Thread.run(Thread.java:1019)
Caused by: java.lang.NoClassDefFoundError: org.apache.http.entity.ContentType
org.apache.http.entity.mime.content.ByteArrayBody.<init>(ByteArrayBody.java:67)
org.apache.http.entity.mime.content.ByteArrayBody.<init>(ByteArrayBody.java:87)
If you are using a library, you need to put it into /libs folder.
EDIT:
download httpmime, httpcore and httpclient library from http://hc.apache.org/downloads.cgi
Use this code to upload image file
HttpClient client = new DefaultHttpClient();
HttpPost postMethod = new HttpPost("http://localhost/Upload/index.php");
File file = new File(filePath);
MultipartEntity entity = new MultipartEntity();
FileBody contentFile = new FileBody(file);
entity.addPart("userfile",contentFile);
StringBody contentString = new StringBody("This is contentString");
entity.addPart("contentString",contentString);
postMethod.setEntity(entity);
client.execute(postMethod);
and in PHP use this code to receive
$uploads_dir = '/Library/WebServer/Documents/Upload/upload/'.$_FILES['userfile']['name'];
if(is_uploaded_file($_FILES['userfile']['tmp_name'])) {
echo $_POST["contentString"]."\n";
echo "File path = ".$uploads_dir;
move_uploaded_file ($_FILES['userfile'] ['tmp_name'], $uploads_dir);
} else {
echo "\n Upload Error";
echo "filename '". $_FILES['userfile']['tmp_name'] . "'.";
print_r($_FILES);
}
Use this code:--
public class HttpMultipartUpload {
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "AaB03x87yxdkjnxvi7";
public String upload(URL url, File file, String fileParameterName,
HashMap<String, String> parameters) throws IOException {
HttpURLConnection conn = null;
DataOutputStream dos = null;
DataInputStream dis = null;
FileInputStream fileInputStream = null;
byte[] buffer;
int maxBufferSize = 20 * 1024;
try {
// ------------------ CLIENT REQUEST
fileInputStream = new FileInputStream(file);
// open a URL connection to the Servlet
// Open a HTTP connection to the URL
conn = (HttpURLConnection) url.openConnection();
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"multipart/form-data;boundary=" + boundary);
dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\""
+ fileParameterName + "\"; filename=\"" + mFileName
+ ".jpg" + "\"" + lineEnd);
dos.writeBytes("Content-Type:image/jpg" + lineEnd);
dos.writeBytes(lineEnd);
// create a buffer of maximum size
buffer = new byte[Math.min((int) file.length(), maxBufferSize)];
int length;
// read file and write it into form...
while ((length = fileInputStream.read(buffer)) != -1) {
dos.write(buffer, 0, length);
}
for (String name : parameters.keySet()) {
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\""
+ name + "\"" + lineEnd);
dos.writeBytes(lineEnd);
dos.writeBytes(parameters.get(name));
}
// send multipart form data necessary after file data...
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
dos.flush();
} finally {
if (fileInputStream != null)
fileInputStream.close();
if (dos != null)
dos.close();
}
// ------------------ read the SERVER RESPONSE
try {
dis = new DataInputStream(conn.getInputStream());
StringBuilder response = new StringBuilder();
String line;
while ((line = dis.readLine()) != null) {
response.append(line).append('\n');
}
System.out.println("Upload file responce:"
+ response.toString());
return response.toString();
} finally {
if (dis != null)
dis.close();
}
}
}
If someone just cannot figure out what is going on with headers, take a look at this article http://develop-for-android.blogspot.com/2014/01/using-volley-in-your-application.html It just saved me the day.
Read the source of the file http. Check this solution:
Call new MultipartEntity:
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
Add request header
heads.put("Content-Type", "image/png;charset=utf-8");

upload file to server in android

i want to upload a file from my android phone to server URL. but the server required login first. how can i do this to upload the file to this server URL with also login URL.
for example:
uploading url:
http://api.someapi.net/v1/medium/14
parameters required: file and title
login url:
http://api.someapi.net/v1/user/login
parameters required: email and password
Check the login credentials by using the code below. If the response is OK send the file to the url using the same code below with different parameters. Else do whatever you want.
URL siteUrl;
try {
siteUrl = new URL("yoururl");
HttpURLConnection conn = (HttpURLConnection) siteUrl
.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setDoInput(true);
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
String content1 = "";
//Here param is the Map<String,String> which holds the value of email and password
Set getkey = param.keySet();
Iterator keyIter = getkey.iterator();
String content = "";
for (int i = 0; keyIter.hasNext(); i++) {
Object key = keyIter.next();
if (i != 0) {
content += "&";
}
content += key + "=" + param.get(key);
}
//Check by printing here
System.out.println(content);
out.writeBytes(content.trim());
out.flush();
out.close();
BufferedReader in = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String line = "";
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
}
Hope it helps
You need Apache's Mime4J jar file on your project (here - http://james.apache.org/download.cgi#Apache_Mime4J )
And do:
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
entity.addPart("file", new InputStreamBody(file, "UPLOAD.jpg"));
request.setEntity(entity);
Tada!

Upload file in Android with outofmemory error

My upload code as below:
String end = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
try {
URL url = new URL(ActionUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
con.setRequestMethod("POST");
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Accept", "text/*");
con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
DataOutputStream ds = new DataOutputStream(con.getOutputStream());
ds.writeBytes(twoHyphens + boundary + end);
ds.writeBytes("Content-Disposition: form-data;" + "name=\"folder\"" + end + end);
ds.write(SavePath.getBytes("UTF-8"));
ds.writeBytes(end);
ds.writeBytes(twoHyphens + boundary + end);
ds.writeBytes("Content-Disposition: form-data;" + "name=\"Filedata\"; filename=\"");
ds.write(FileName.getBytes("UTF-8"));
ds.writeBytes("\"" + end);
ds.writeBytes(end);
FileInputStream fStream = new FileInputStream(uploadFilepath+""+FileName);
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int length = -1;
int pro = 0;
while((length = fStream.read(buffer)) != -1) {
ds.write(buffer, 0, length);
}
ds.writeBytes(end);
ds.writeBytes(twoHyphens + boundary + twoHyphens + end);
fStream.close();
ds.flush();
InputStream is = con.getInputStream();
int ch;
StringBuffer b = new StringBuffer();
while((ch = is.read()) != -1) {
b.append((char)ch);
}
ds.close();
}
catch(Exception e) {
e.printStackTrace();
}
It can works while upload a smaller file.
But while more 16 mb, it will upload fail and show the OutOfMemory error.
I think it cause by put all data in buffer.
So I want to make it to send data while buffer save 1024 bytes.
But I no idea to do that.
May anyone help me to do it?
brian, you should add
con.setChunkedStreamingMode(0);
before
DataOutputStream ds = new DataOutputStream(con.getOutputStream());
if your server could support chunked mode, or add
con.setFixedLengthStreamingMode(packet_size);
where packet_size = upload_file_size + header_size.
You should confirm at what point your error occurs. I suspect that it's during reading the response. In this case, it seems that server may be responding with a lot of data that you place in the StringBuffer. Do you actually need to consume the entire response and keep it in memory? If it's a file, save it rather than keeping in memory.
I did some more research and here is one other possibility. Android JVM by default has 16mb max heap. See this for some details.
On the other hand, if your server does not actually consume the data, most of it will reside on the client. So if you have more than max heap of data the client will fail.
So I suspect that your server just does not read the data from the stream.
The following class (which is a snippet of relevant parts of your code) illustrates the problem. Run it on any JVM like following:
java -Xmx16m -cp . Test
and it will produce OOM very quickly. In fact, much earlier than expected.
import java.io.*;
import java.net.*;
public class Test {
public static void main(String argv[]) throws Exception {
new Thread() {
public void run() {
try {
new ServerSocket(12000).accept();
} catch (Throwable t) {
t.printStackTrace();
}
}
}.start();
URL url = new URL("http://localhost:12000/");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
DataOutputStream ds = new DataOutputStream(con.getOutputStream());
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
for (int i=0;i<100000;i++) {
ds.write(buffer, 0, 1024);
System.out.println("Written chunk " + i);
}
ds.flush();
}
}

POST with Basic Auth fails on Android but works in C#

I have an app I am developing that requires me to post data to a 3rd party API. I have been struggling with authentication since the beginning and kept putting off further and further, and now I'm stuck.
I have tried using an Authenticator, but have read all about how there appears to be a bug in certain Android versions: Authentication Example
I have tried several different options, including the Apache Commons HTTP Library with no success. After all of this, I decided to make sure that the API wasn't the pain point. So I wrote a quick WinForms program to test the API, which worked perfectly on the first try. So, the idea that I'm working from and the API I working with both seem fine, but I am in desperate need of some guidance as to why the Java code isn't working.
Examples follow:
C# Code that works everytime:
System.Net.ServicePointManager.Expect100Continue = false;
// Create a request using a URL that can receive a post.
WebRequest request = WebRequest.Create(addWorkoutUrl);
// Set the Method property of the request to POST.
request.Method = "POST";
// Create POST data and convert it to a byte array.
string postData = "distance=4000&hours=0&minutes=20&seconds=0&tenths=0&month=08&day=01&year=2011&typeOfWorkout=standard&weightClass=H&age=28";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Set the ContentType property of the WebRequest.
request.Headers["X-API-KEY"] = apiKey;
request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes("username:password"));
request.ContentType = "application/x-www-form-urlencoded";
// Set the ContentLength property of the WebRequest.
request.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStream = request.GetRequestStream();
// Write the data to the request stream.
dataStream.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStream.Close();
// Get the response.
WebResponse response = request.GetResponse();
// Display the status.
MessageBox.Show(((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
MessageBox.Show(responseFromServer);
// Clean up the streams.
reader.Close();
dataStream.Close();
response.Close();
Java code for Android that currently returns a 500:Internal Server Error, though I believe this is my fault.
URL url;
String data = "distance=4000&hours=0&minutes=20&seconds=0&tenths=0&month=08&day=01&year=2011&typeOfWorkout=standard&weightClass=H&age=28";
HttpURLConnection connection = null;
//Create connection
url = new URL(urlBasePath);
connection = (HttpURLConnection)url.openConnection();
connection.setConnectTimeout(10000);
connection.setUseCaches(false);
connection.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
connection.setRequestProperty("Accept","*/*");
connection.setRequestProperty("X-API-KEY", apiKey);
connection.setRequestProperty("Authorization", "Basic " +
Base64.encode((username + ":" + password).getBytes("UTF-8"), Base64.DEFAULT));
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", "" + Integer.toString(data.getBytes("UTF-8").length));
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.write(data.getBytes("UTF-8"));
wr.flush();
wr.close();
statusCode = connection.getResponseCode();
statusReason = connection.getResponseMessage();
//At this point, I have the 500 error...
I figured out the problem, and the solution finally after stumbling across the root cause as mentioned in the comment above.
I was using Base64.encode() in my example, but I needed to be using Base64.encodeToString().
The difference being that encode() returns a byte[] and encodeToString() returns the string I was expecting.
Hopefully this will help somebody else who is caught by this.
Here's a nicer method to do to the POST.
install-package HttpClient
Then:
public void DoPost()
{
var httpClient = new HttpClient();
var creds = string.Format("{0}:{1}", _username, _password);
var basicAuth = string.Format("Basic {0}", Convert.ToBase64String(Encoding.UTF8.GetBytes(creds)));
httpClient.DefaultRequestHeaders.Add("Authorization", basicAuth);
var post = httpClient.PostAsync(_url,
new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "name", "Henrik" },
{ "age", "99" }
}));
post.Wait();
}
I have tried this in java
import java.io.*;
import java.net.*;
class download{
public static void main(String args[]){
try{
String details = "API-Key=e6d871be90a689&orderInfo={\"booking\":{\"restaurantinfo\":{\"id\":\"5722\"},\"referrer\":{\"id\": \"9448476530\" }, \"bookingdetails\":{\"instructions\":\"Make the stuff spicy\",\"bookingtime\": \"2011-11-09 12:12 pm\", \"num_guests\": \"5\"}, \"customerinfo\":{\"name\":\"Ramjee Ganti\", \"mobile\":\"9345245530\", \"email\": \"sajid#pappilon.in\", \"landline\":{ \"number\":\"0908998393\",\"ext\":\"456\"}}}}";
Authenticator.setDefault(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("admin", "1234".toCharArray());
}
});
HttpURLConnection conn = null;
//URL url = new URL("http://api-justeat.in/ma/orders/index");
URL url = new URL("http://api.geanly.in/ma/order_ma/index");
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput (true);
conn.setRequestMethod("POST");
//conn.setRequestMethod(HttpConnection.POST);
DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
outStream.writeBytes(details);
outStream.flush();
outStream.close();
//Get Response
InputStream is = conn.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
StringBuffer response = new StringBuffer();
while((line = rd.readLine()) != null) {
System.out.println(line);
}
rd.close();
System.out.println(conn.getResponseCode() + "\n\n");
}catch(Exception e){
System.out.println(e);
}
}
}
this could help.

Categories

Resources