I am uploading a file to server using the HttpsURLConnection class in Android. I am using PUT method. In normal circumstances it is working fine, but when I am attempting to write a file to the folder in a server, which does not have write permissions, the write method should fail and then when I use getResponseCode() I should recieve a 403 Forbidden response from the server. I have to use the 403 response to show a "No Permissions" message to the user.
Now, when the file size that I upload is less than the buffer size (16K), the write method returns and the getResponseCode returns 403. However, when the file size is bigger (which is usually the case), the program stucks in the write method and thus does not reach the getResponseCode at all. This is happening in all Android devices including the simulator.
Here is the code snippet:
HttpsURLConnection connection = getSSLChannel();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setConnectTimeout(connectionTimeout);
connection.setReadTimeout(readTimeout);
connection.setUseCaches(false);
connection.setRequestMethod("PUT");
connection.setFixedLengthStreamingMode(size);
connection.setRequestProperty("Connection", "Keep-Alive");
OutputStream out = connection.getOutputStream();
try{
int count =0;
long progress = 0;
byte[] chunkData = new byte[16384];
long fileSize = localFile.getLocalFileSize();
bis = getSourceBufferedStream();
while (((nRead = bis.read(chunkData)) != -1)) {
if(nRead < chunkData.length){
byte[] newBytes = Arrays.copyOf(chunkData, nRead);
out.write(newBytes, 0, nRead);
count++;
}else{
out.write(chunkData, 0, nRead);
count++;
}
}
out.flush();
}finally{
if(out != null){
try {
out.close();
out = null;
} catch (IOException e) {
e.printStackTrace();
}
}
int resCode = connection.getResponseCode();
System.out.println("Res Code :" + resCode);
}
I have put a debug pointer in the finally block. But the app never enters the finally block.
Some points:
1. The server does not support multipart upload.
2. There is no Separate web service to find out, if the folder has write permissions on the server.
3. In iOS this works fine. I am able to receive 403.
4. I have tried with Fiddler, which also gives me a 403.
5. Also, in fast networks (if the server is located in the local wifi domain), I am able to get 403 sometimes.
What am I doing wrong ? Please help !
Thanks.
Related
I'm getting mad about HttpUrlConnection an Outputstreams.
I just want to send a string to php and from there put it in a database.
The last two nights I read nearly thousand guides and threads about sending data to webserver, tried almost everything I read - and it's still not working.
protected void phpPOST(final String ServerURL, final String StringToPost)
{
Thread newthread = new Thread() {
public void run() {
//1. set URL and connect to server
HttpURLConnection connection = null;
try {
URL server = new URL(ServerURL + "teilnehmer_update.php");
connection = (HttpURLConnection) server.openConnection();
//2. set method to POST and enable output
connection.setRequestMethod("POST");
connection.setDoOutput(true);
//3. open outputstream and send string to url
OutputStreamWriter outstream = new OutputStreamWriter(connection.getOutputStream());
outstream.write(StringToPost);
outstream.flush();
outstream.close();
} catch (Exception e) {
e.printStackTrace();
Log.i("Error!", "ERROR is " + e);}
finally {if (connection != null) {connection.disconnect();}}
}
};
newthread.start();
}
I reduced my php-file to very simple, to secure it's not a php problem. Tried from browser, works fine.
<?php
// for testing, should create an empty file when no no data is recieved.
$file = fopen('test.txt', 'w');
$id = $_POST['id'];
fwrite($file, $id);
fclose($file);
?>
I don't get any errors - so, I don't know what to fix. LogCat is also empty.
I think the Problem is something with the Outputstream (also tried versions BufferdOS,OS,DataOS). As far as I get it, the connection is should open when I create the OS and write to it. - But, in my case it does not do anything...
Any ideas what's wrong with the code ?
Thanks
The code below works great if I connect to what seems to be Apache servers, however when I try to connect to my .Net server it throws an error. I am guessing it is a header requirement, but I can not seem to get a successful response no matter what I try.
public String Download(String Url)
{
String filepath=null;
try {
//set the download URL, a url that points to a file on the internet
//this is the file to be downloaded
URL url = new URL(Url);
//create the new connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
//set up some things on the connection
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
//and connect!
urlConnection.connect();
//set the path where we want to save the file
//in this case, going to save it on the root directory of the sd card.
File SDCardRoot = Environment.getExternalStorageDirectory();
//create a new file, specifying the path, and the filename
//which we want to save the file as.
String filename= "effortback.png"; // you can download to any type of file ex:.jpeg (image) ,.txt(text file),.mp3 (audio file)
Log.i("Local filename:",""+filename);
File file = new File(SDCardRoot + "/",filename);
//=====================================
if(file.createNewFile())
{
file.createNewFile();
}
//=====================================
//this will be used to write the downloaded data into the file we created
FileOutputStream fileOutput = new FileOutputStream(file);
//this will be used in reading the data from the internet
InputStream inputStream = urlConnection.getInputStream();
//=====================================
//this is the total size of the file
int totalSize = urlConnection.getContentLength();
//variable to store total downloaded bytes
int downloadedSize = 0;
//=====================================
//create a buffer...
byte[] buffer = new byte[2048];
int bufferLength = 0; //used to store a temporary size of the buffer
//now, read through the input buffer and write the contents to the file
while ( (bufferLength = inputStream.read(buffer)) > 0 ) {
//add the data in the buffer to the file in the file output stream (the file on the sd card
fileOutput.write(buffer, 0, bufferLength);
//add up the size so we know how much is downloaded
downloadedSize += bufferLength;
//this is where you would do something to report the prgress, like this maybe
Log.i("Progress:","downloadedSize:"+downloadedSize+"totalSize:"+ totalSize) ;
}
//close the output stream when done
fileOutput.close();
if(downloadedSize==totalSize) filepath=file.getPath();
//catch some possible errors...
} catch (MalformedURLException e) {
e.printStackTrace();
Log.i("URL-ERROR:",e.toString());
} catch (IOException e) {
filepath=null;
e.printStackTrace();
Log.i("IO-ERROR:",e.toString());
}
Log.i("filepath:"," "+filepath) ;
return filepath;
}
Errors range from:
java.io.FileNotFoundException //I always get this with either one of the below
org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1061)
//or this one below
libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionTmpl.java:186)
It seems that no matter what I try I can not get it to work. Again, it works if I try to download an image from Google or some other sites, but not all, but definitely not mine (.Net). What am I missing here? Please help.
You can get a FileNotFoundException from HttpUrlConnection (and OkHttpClient) if your server returns >= HTTPStatus.BAD_REQUEST (400). You should check the status code first to check what stream you need to read.
int status = connection.getResponseCode();
if(status >= HttpStatus.SC_BAD_REQUEST)
in = connection.getErrorStream();
else
in = connection.getInputStream();
HttpStatus deprecated. Latest syntax seems to be:
InputStream inputStream;
int status = urlConnection.getResponseCode();
if (status != HttpURLConnection.HTTP_OK) {
inputStream = urlConnection.getErrorStream();
}
else {
inputStream = urlConnection.getInputStream();
}
Had the same problem, solved like you said:
urlConnection.setDoOutput(false);
Note that you must set it to false because it's true by default.
Note that you must set it to false because Volley HurlStack was setting it to true.
Take care ;)
EDIT:
I've just checked the code and by default it's false as #Abraham Philip said. But I had to set it to false because if I called getDoOutput() it was returning true. I found that Volley HurlStack was setting it to true. So, my conclusion is, setDoOutput to false and it will work.
package java.net;
public abstract class URLConnection {
...
/**
* Specifies whether this {#code URLConnection} allows sending data.
*/
protected boolean doOutput;
...
public boolean getDoOutput() {
return doOutput;
}
public void setDoOutput(boolean newValue) {
checkNotConnected();
this.doOutput = newValue;
}
I faced an issue, where executing a HEAD-Request threw a FileNotFoundException.
The reason is, that a response to a HEAD does not have a body and therefore a call to getInputStream throws a FileNotFoundException.
So if you are executing a HEAD, you should not try to get the InputStream.
My Android tablet application does not work with ICS due to a Login problem. When I looked at my code and ran it under debug mode on an ICS tablet, I see the problem but I don't understand it. The code functions correctly on all Honeycomb models that i have tested and in fact I have two tablets hooked up to my computer (one Samsung Galaxy Tab running 3.2, and a Motorola Xoom wifi running 4.0.3) and the code fails on ICS and works on HC.
The failure is a Socket Timeout exception. The timeout was 2000ms, but I upped it to 100000ms to test and it had no impact.
Using the browser on the ICS tablet, I can go to the URL and it responds, so it doesn't appear to be network related.
I am running on a background thread using AsyncTask.
Slurp just takes all of the input from the InputStream and using StringBuilder creates a string representation. Its not actually useful in this request but I added it to see what the server was replying with.
I am POSTing to the page the same way a user authenticates using the form, which is why I am using x-www-form-urlencoded.
Again, this code functions perfectly on Honeycomb but fails on ICS.
The code makes a connection but fails when it asks for a response from the server, almost like the server is still waiting for something... anyway, here is the code:
static public String authenticate(String service_url, String username, String password) throws IOException {
if (username == null || password == null)
throw new IOException();
String charset = "UTF-8";
String query = String.format("Email=%s&Password=%s",URLEncoder.encode(username, charset),URLEncoder.encode(password, charset));
byte [] data = query.getBytes(charset);
URL url = new URL(service_url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Length", Integer.toString(data.length));
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setReadTimeout(5000); // 2 second timeout.
try {
connection.connect();
DataOutputStream pw = new DataOutputStream (connection.getOutputStream());
pw.writeBytes(query);
pw.flush();
pw.close();
int code = connection.getResponseCode(); //SOCKET TIMEOUT HERE
if (code == 200 || code == 302)
{
InputStream is = connection.getInputStream();
String value = slurp(is);
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
if (cookies == null)
throw new IOException();
for (String cookie : cookies) {
if (cookie.startsWith("cpms")) {
cookieTime = new DateTime(); //crazy but the expires time in the cookie is not actually accurate.
return cookie; // this is the only correct path out.
}
}
}
else
Logger.e(StaticUtils.class, "Invalid response code while logging in: " + code);
}
catch (IOException ioe)
{
Logger.e(StaticUtils.class, ioe);
throw ioe; // log it and then throw it back.
} finally {
connection.disconnect();
}
return null;
}
I can't find an example of a simple FTP access of a file anywhere, and the FTPClient class (which a couple of examples use) doesn't appear in the Android Class Index. I've got http access working, but how do I do a simple FTP get? All I want to do is download (for example):
ftp://tgftp.nws.noaa.gov/data/observations/metar/stations/KABQ.TXT
It shouldn't require login, change directory, etc.
Just giving that URL to the http access methods don't seem to work.
This is similar to the question at:
unable to read file from ftp in android?
I tried a simple:
StringBuilder response = new StringBuilder();
URLConnection ftpConn;
try {
URL netUrl = new URL("ftp://tgftp.nws.noaa.gov/data/observations/metar/stations/KABQ.TXT");
ftpConn = netUrl.openConnection();
BufferedInputStream bufRd = new BufferedInputStream(ftpConn.getInputStream());
int temp;
while ((temp = bufRd.read()) != -1) {
response.append(temp);
}
bufRd.close();
} catch (Exception e) {
return "Failure";
}
but it gets an exception on getInputStream:
Unable to connect to server: Unable to configure data port
Also, there must be a more intelligent way to pull the data out of the stream buffer than byte-by-byte, isn't there? I can't find that either.
Lastly, I need to do both http and ftp access, is there any reason not to use URLConnection for both access types? or is it better to use HttpConnection for http and URLConnection for ftp?
Thanks!
Whew! I finally got it going. I gave up on the simple way that works in webOS and WPF/C# where you can just do a ftp:://... you have to use the FTPClient package.
After fixing the library access (Project | Properties | Java Build Path | Libraries | Add JARs...) I fiddled with the calls until it started working. Here's the sequence of my FTPClient calls. It wouldn't work until I set it in passive mode.
mFTPClient = new FTPClient();
mFTPClient.connect("tgftp.nws.noaa.gov");
mFTPClient.login("anonymous","nobody");
mFTPClient.enterLocalPassiveMode();
mFTPClient.changeWorkingDirectory("/data/forecasts/taf/stations");
InputStream inStream = mFTPClient.retrieveFileStream("KABQ.TXT");
InputStreamReader isr = new InputStreamReader(inStream, "UTF8");
And I also found on the web someplace an answer to the 'byte-by-byte' question. This seems to work to convert an InputStream type directly to String type:
String theStr = new Scanner(inStream).useDelimiter("\\A").next();
I also looked for a simple ftp download example without using of 3rd party libs. Didn't find any, so post my solution here.
URLConnection by default uses user name 'anonymous' with empty password which is not accepted by many ftp servers, as they require e-mail as the password for 'anonymous'.
To use the following code in your app, just add try..catch and make sure that reading from stream isn't block UI thread.
URL url = new URL("ftp://ftp.mozilla.org/README");
URLConnection cn = url.openConnection();
cn.setRequestProperty ("Authorization", "Basic " + Base64.encodeToString("anonymous:a#b.c".getBytes(), Base64.DEFAULT));
final File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
FileOutputStream fos = new FileOutputStream(dir.getPath() + "/README");
InputStream is = cn.getInputStream();
int bytesRead = -1;
byte[] buf = new byte[8096];
while ((bytesRead = is.read(buf)) != -1) {
fos.write(buf, 0, bytesRead);
}
if(is != null)is.close();
if(fos != null){ fos.flush(); fos.close(); }
Hope this will save you some time.
I am desperatly trying to find a soulution for WiFi/GSM connection switch while uploading files via HttpUrlConnection.
I have seen people struggling with similar problems but still couldn't find a working solution.
Here is the code:
try {
/* Opening file stream and so... */
URL url = new URL(url_to_send);
connection = (HttpURLConnection) url.openConnection();
// Allow Inputs & Outputs
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
// connection.setConnectTimeout(1000); // doesn't have any effect
// connection.setReadTimeout(1000);
connection.setChunkedStreamingMode(maxBufferSize);
// Enable POST method
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form-data; charset=utf-8; boundary=" + BOUNDARY);
bytesAvailable = file_input.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// Read file
bytesRead = file_input.read(buffer, 0, bufferSize);
already_send += bytesRead;
while (bytesRead > 0) {
/* Updating some progressbar values */
/* HERE IS THE LINE WHERE THREAD HANGS */
output_byte.write(buffer, 0, bufferSize);
bytesAvailable = file_input.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = file_input.read(buffer, 0, bufferSize);
already_send += bytesRead;
}
output_byte.writeBytes(endBoundary);
output_byte.flush();
// Responses from the server (code and message)
serverResponseCode = connection.getResponseCode();
serverResponseMessage = connection.getResponseMessage();
} catch (FileNotFoundException e) {
Log.e(TAG, "Uploaded file not found");
e.printStackTrace();
} catch (MalformedURLException e) {
Log.e(TAG, "Malformed URL");
e.printStackTrace();
} catch (IOException e) {
Log.e(TAG, "Uploading problem: " + e.toString());
e.printStackTrace();
} finally {
/* closing connection & streams */
}
The problem is that whenever i switch from WiFi to GSM or from GSM to WiFi during the uploading loop the Thread freezes on the line marked in the code:
output_byte.write(buffer, 0, bufferSize);
I have tried using connection.disconnect() which should throw some kind of a IOException (it does in normal situations but not in this particular case)
I have tried using OutputStream.close() called from second thread but it also hangs the second (main UI) thread making me unable to even close the app.
I have been searching for something like connection.isAvailable() which I could call before the ouptut_byte.write but I couldn't find anything useful in HttpUrlConnection class.
I have tried monitoring the WiFi state but the uploading thread seems to be faster than wifi state change and it blocks before I am able to provide it the current wifi state change. (also tried the ConnectivityManager - no difference)
Finally I have tried to kill the thread by adding volatile flag to my Thread variable and using Thread.interrupt() - no effect. Thread is still alive and it's state is "RUNNING".
Have you got any ideas how Can I solve the problem? I would be really glad if I could catch any type of exception in this thread after is blocks.
I got into exactly the same situation as Shaar did. I have an application for the file upload on the server. On the HTC Desire A8181 (Android 2.2.2) when I turn off the mobile data connection (or switch it into wifi connection) when the appliaction writes into output stream (I am writing there 512 kB data blocks) it just blocks in there for approximately 16 minutes. After these 16 minutes I get an Socket Exception (which is not always the same).
When I try it on other device or on the same device (HTC Desire A8181) but on wifi connection, the exception is thrown right after the connection is closed. So I think it is a bug. And one solution that I think about is to upload in a Thread and when there is a change in connection (BroadcastReceiver) then let the thread die (for these 16 minutes) and start another Thread. It is not a nice solution because of living threads in the system but I don't know any other solution...
The example code is the same as in the beginning of this thread. I also tried both the Apache's HttpClient and the HttpUrlConnection.
You might need to use HttpClient: there are methods to set the socket timeout that may do what you want, which is in effect ending the connection and throwing some kind of exception when the network connection is broken by the switch.
Late response, but I used the following Q&A Android, How to handle change in network (from GPRS to Wi-fi and vice-versa) while polling for data to handle this. Have the upload in its own thread and on a network failover, restart the thread and upload.
Thank you Femi for your answer!
I've tried that solution today and it makes no difference at all.
I have used a custom multipartentity http://toolongdidntread.com/android/android-multipart-post-with-progress-bar/ found in here mixed with some apache libraries: httpclient, httpcore and httpmime
Here is the new code:
File uploaded_file = new File(mess.getPath());
CustomMultiPartEntity multipart = new CustomMultiPartEntity(new CustomMultiPartEntity.ProgressListener() {
#
Override
public void transferred(long num) {
Log.v(TAG, num * 100 / total_size + " !");
// updating progressbar
}
});
// adding some fields to multipart
multipart.addPart(file_field, new FileBody(uploaded_file));
total_size = multipart.getContentLength();
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setSoTimeout(params, 5000);
HttpConnectionParams.setConnectionTimeout(params, 5000);
httpClient = new DefaultHttpClient(params);
httpContext = new BasicHttpContext();
httpPost = new HttpPost(url_to_send);
httpClient.getParams().setParameter("http.protocol.content-charset", "UTF-8");
httpPost.setEntity(multipart);
// Responses from the server (code and message)
HttpResponse response = httpClient.execute(httpPost);
serverResponseCode = response.getStatusLine().getStatusCode();
serverResponseMessage = response.getStatusLine().getReasonPhrase();
After WiFi / GSM
switch the Thread acts the same blocking the execution.I have also tried setting the timeouts this way:
httpclient.getParams().setParameter("http.socket.timeout", new Integer(1000));
httppost.getParams().setParameter("http.socket.timeout", new Integer(5000));
Still no effects...
Also tried using httpPost.abort() and httpClient.getConnectionManager().closeExpiredConnections() and httpClient.getConnectionManager().shutdown().
No effect at all, thread is not throwing any exceptions, it remains in state RUNNING but it is blocked and dead.
I think that there is nothing we can do about it.
Strange thing is that when I turn the wifi back on the thread "wakes up" and continue uploading but it is pointless if I would have to get back to wifi area in order to complete the uploading process.
PS. Both examples were tested on 3 real devices: Samsung Spica runing 2.1, Samsung Galaxy S running 2.2.1 and Motorola Defu runing also 2.2.1. While Motorola and Spica are having problems with the described situation the Galaxy S behaves differently - it throws an IOException which is something that I would expect from the other ones.