Android: uploading a video to server - java

This question is a little bit long, so there might be confusions and in any case if you have, please don't hesitate to let me know..
I was trying to make a code to upload a video to the web server. In order to do so, I split the code's roles into three (in general, not including the details):
an activity that uploads a video and an inner class called VideoUploader that extends AsyncTask to execute the upload process,
a manager class called VideoManager.java that deals with uploading a video with the POST method,
and the PHP part that takes the POST video and stores it in the server.
Here is the uploadVideo() method inside the activity.
public void uploadVideo(final String stringPath) {
class VideoUploader extends AsyncTask<Void, Void, String> {
ProgressDialog pDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(UploadVideoActivity.this);
pDialog.setMessage("Uploading");
pDialog.setIndeterminate(true);
pDialog.setProgress(0);
pDialog.show();
final int totalProgressTime = 100;
final Thread thread = new Thread() {
#Override
public void run() {
int jumpTime = 0;
while(jumpTime < totalProgressTime) {
try {
sleep(200);
jumpTime += 5;
pDialog.setProgress(jumpTime);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
pDialog.dismiss();
}
#Override
protected String doInBackground(Void... params) {
VideoManager videoManager = new VideoManager(getApplicationContext());
final String msg = videoManager.getUploadVideoResponse(stringPath);
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
return msg;
}
}
VideoUploader uv = new VideoUploader();
uv.execute();
}
The following code is the VideoManager.java class.
public class VideoManager {
private Context context;
private int serverResponseCode;
private String urlUploadVideo = ServerURL.UPLOAD_VIDEO;
public VideoManager(Context context) {
this.context = context;
}
public String getUploadVideoResponse(String filePath) {
HttpURLConnection conn = null;
DataOutputStream dos = null;
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
/*
* a buffer is a temporary memory area in which data is stored while it is being processed
* or transferred, especially one used while streaming video or downloading audio.
* here the maximum buffer size is 1024 bytes * 1024 bytes = 1048576 bytes ≈ 1.05 megabytes.
*/
int maxBufferSize = 1 * 1024 * 1024;
File sourceFile = new File(filePath);
// check if the source file is a proper file.
if (!sourceFile.isFile()) {
Toast.makeText(context, "File does not exist.", Toast.LENGTH_SHORT).show();
return null;
}
try {
// an input stream that reads bytes from a file
FileInputStream fileInputStream = new FileInputStream(sourceFile);
URL url = new URL(urlUploadVideo);
conn = (HttpURLConnection) url.openConnection();
// allow the url connection input
conn.setDoInput(true);
// allow the url connection output
conn.setDoOutput(true);
// not allow the url connection to use cache
conn.setUseCaches(false);
conn.setRequestMethod("POST");
// set the value of the requested header field
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("ENCTYPE", "multipart/form-data");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
conn.setRequestProperty("myFile", filePath);
// a data output stream lets an application write primitive Java data types to an output stream in a portable way.
// an application can then use a data input stream to read the data back in.
dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"myFile\";filename=\"" + filePath + "\"" + lineEnd);
dos.writeBytes(lineEnd);
bytesAvailable = fileInputStream.available();
Log.i("Huzza", "Initial .available : " + bytesAvailable);
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 + twoHyphens + lineEnd);
serverResponseCode = conn.getResponseCode();
fileInputStream.close();
// flush the data output stream.
dos.flush();
dos.close();
} catch (MalformedURLException ex) {
ex.printStackTrace();
} catch (Exception e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
}
// if the server responds OK
String response;
if (serverResponseCode == 200) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
bufferedReader.close();
} catch (IOException e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
}
response = sb.toString();
return response;
} else {
response = "An error occurred while uploading the video.";
return response;
}
}
}
And finally this is the PHP side that takes the video and stores it in the server.
<?php
$base_location = dirname(__FILE__);
$directory = '/uploaded_videos/';
$location = $base_location . $directory;
if($_SERVER['REQUEST_METHOD'] == 'POST') {
$file_name = $_FILES['video']['name'];
$file_size = $_FILES['video']['size'];
$file_type = $_FILES['video']['type'];
$temp_name = $_FILES['video']['tmp_name'];
move_uploaded_file($temp_name, $location.$file_name);
} else {
echo $location;
}
?>
If I run the programme and execute the uploadVideo() method, I manage to get the string value from the public String getUploadVideoResponse(String filePath) method like below.
From my diagnosis, now that I get the string response, the server response code is 200, which means that the request was successful. But I still fail to have the video file itself in the server. Can any superman find out the real matter here?

Related

Instead of JSON data link is returning .txt file how to get JSON data in android

I am using a link when I open that link in the browser it generates a json.txt file and downloads it. I want to fetch this txt file in an android application and want to get JSON data from this txt file.
new DownloadFileFromURL().execute("your_file_downloadable_url");
class DownloadFileFromURL extends AsyncTask<String, String, String> {
/**
* Before starting background thread
* */
#Override
protected void onPreExecute() {
super.onPreExecute();
System.out.println("Starting download");
}
/**
* Downloading file in background thread
* */
#Override
protected String doInBackground(String... f_url) {
int count;
try {
String root = Environment.getExternalStorageDirectory().toString();
System.out.println("Downloading");
URL url = new URL(f_url[0]);
URLConnection conection = url.openConnection();
conection.connect();
// getting file length
int lenghtOfFile = conection.getContentLength();
// input stream to read file - with 8k buffer
InputStream input = new BufferedInputStream(url.openStream(), 8192);
// Output stream to write file
OutputStream output = new FileOutputStream(root+"/downloadedfile.txt");
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// writing data to file
output.write(data, 0, count);
}
// flushing output
output.flush();
// closing streams
output.close();
input.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
/**
* After completing background task
* **/
#Override
protected void onPostExecute(String file_url) {
System.out.println("Downloaded");
pDialog.dismiss();
}
}

URLConnection time out issue

I am working in an android application and I am downloading a file from an url. Every thing works fine, but when the internet connection goes in between(After opening a connection) the downloading time out never occurs and the connection never ends.
Suggest me a solution to solve this issue
**URL url = new URL("fileURL");
URLConnection connection = url.openConnection();
connection.setConnectTimeout(5000);
File file = new File(context.getFilesDir(), "" + filename);
// getting file length
int lenghtOfFile = connection.getContentLength();
// input stream to read file - with 8k buffer
InputStream input = new BufferedInputStream(url.openStream(), 8192);
// Output stream to write file
OutputStream output = new FileOutputStream(file);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
int status = (int) ((total * 100) / lenghtOfFile);
publishProgress("" + status);
// writing data to file
output.write(data, 0, count);
}
// flushing output
output.flush();
// closing streams
output.close();
input.close()**
You can use Retrofit Library to download files from server,
Retrofit uses OkHttp internally
Please refer below URL,
https://futurestud.io/tutorials/retrofit-2-how-to-download-files-from-server
final FileDownloadService downloadService =
ServiceGenerator.createService(FileDownloadService.class);
Call<ResponseBody> call =
downloadService.downloadFileWithDynamicUrlSync(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, final Response<ResponseBody>
response) {
if (response.isSuccessful()) {
Log.d(TAG, "server contacted and has file");
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
boolean writtenToDisk = writeResponseBodyToDisk(FileDownloadActivity.this, response.body(), null);
Log.d(TAG, "file download was a success? " + writtenToDisk);
return null;
}
}.execute();
} else {
Log.d(TAG, "server contact failed");
}
}
And you can also use #Streaming annotation for large files. Retrofit will handle the large file download also

Multipart post of xlsx get corrupted via HTTPUrlConnection

I am trying to post xlsx file via HTTPUrlconnection, on receiving end I got the file but while opening it in MS Excel its saying that file is corrupted and repairing required . My code snippet for multipart post
class MultipartUtility {
private final Logger log = getLogger(MultipartUtility.class.getName());
private static final String CRLF = "\r\n";
private static final String CHARSET = "UTF-8";
private static final int CONNECT_TIMEOUT = 1500000;
private static final int READ_TIMEOUT = 1000000;
private final HttpURLConnection connection;
private final OutputStream outputStream;
private final PrintWriter writer;
private final String boundary;
// for log formatting only
private final URL url;
private final long start;
public MultipartUtility(final String strUrl) throws IOException {
start = currentTimeMillis();
URL url = new URL(strUrl);
this.url = url;
boundary = "---------------------------" + currentTimeMillis();
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(CONNECT_TIMEOUT);
connection.setReadTimeout(READ_TIMEOUT);
connection.setRequestMethod("POST");
connection.setRequestProperty("Accept-Charset", CHARSET);
connection.setRequestProperty("Content-Type","multipart/form-data; boundary=" + boundary);
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
outputStream = connection.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, CHARSET),true);
}
public void addFilePart(final String filePath)throws IOException {
String fieldName = "content";
File uploadFile = new File(filePath);
final String fileName = uploadFile.getName();
writer.append("--").append(boundary).append(CRLF)
.append("Content-Disposition: form-data; name=\"")
.append(fieldName).append("\"; filename=\"").append(fileName)
.append("\"").append(CRLF).append("Content-Type: ")
.append(guessContentTypeFromName(fileName)).append(CRLF)
.append("Content-Transfer-Encoding: binary").append(CRLF)
.append(CRLF);
writer.flush();
outputStream.flush();
try (final FileInputStream inputStream = new FileInputStream(uploadFile);) {
final byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
writer.append(CRLF);
}
public HashMap<Object, Object> finish() throws IOException {
writer.append(CRLF).append("--").append(boundary).append("--").append(CRLF);
writer.close();
final int status = connection.getResponseCode();
if (status != HTTP_OK) {
throw new IOException(format("{0} failed with HTTP status: {1}",url, status));
}
try (final InputStream is = connection.getInputStream()) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String sResponse;
StringBuilder responseBuilder = new StringBuilder();
while ((sResponse = reader.readLine()) != null) {
responseBuilder = responseBuilder.append(sResponse);
}
HashMap respMap = new HashMap();
respMap.put("RESP_MSG", responseBuilder.toString());
respMap.put("RESP_CODE", status);
respMap.put("RESP_SIZE", responseBuilder.toString().length());
log.log(INFO,format("{0} took {4} ms", url,(currentTimeMillis() - start)));
log.log(INFO,"status::::::"+status);
return respMap;
} finally {
connection.disconnect();
}
}
}
The problem is with your addFilePart method, in the end, you are appending "writer.append(CRLF)"
when the number of files is one you should not add this at the end because it will add extra 2 bits to you file size you are sending over the network which is causing you xlxs file to get corrupted.
below is the code if you want to add one file or multiple files.
in this, we are appending CRLF(to separate the files) to the writer to all the files except the last one.
public void addFilePart(List<String> files) {
if (nonNull(files) && !files.isEmpty()) {
AtomicInteger counter = new AtomicInteger(0);
files.forEach(file -> {
try {
int i = counter.incrementAndGet();
this.addFilePart(file); // your add file part method
if (files.size() != i) {
writer.append(CRLF);
}
writer.flush();
} catch (IOException e) {
}
});
}
}
I tried to execute your code and was able to successfully upload file from java program.
The problem you reported is i guess due to content-type of the file. If you try to upload .xlsx (MS Excel 2007) it deforms the data and need to recover before we read uploaded file.
If you try to upload .xls file, it uploads properly without any deform and MS excel open this file without any warning/error.
So i would suggest to play around
writer.append(
"Content-Type: "
+ "application/x-excel")
to find the correct content-type refer to :
https://www.codeproject.com/Questions/481262/contentplustypeplusforplusxlsxplusfile
best of luck

Java Android client java server code blocks occasionally , how to solve

[I am asking this because I have checked previous questions and none have answered my questions specific to the code here]
I am relatively new to client server coding. I have Android as client and Java server. The system at the moment works like this:: Android client selects/loads a Jpeg image from sdcard, sends int size, string text and image file to server and server sends back integer and a text file with data back to client
My problem at the moment it works perfectly (randomly) only roughly ~60% of the runs. The remainder of the time it blocks permanently and I have to restart server to continue. [Certainly, a little over half of the time, the client-server system sends and receives without a glitch, but ~40% to 45% (permanent block) failure rate is unacceptable]
When it blocks there is no crash dump, stacktrace or error to read. I have searched previous similar blocking questions and tried to close sockets and inputstream/outputstream and wrappers in different orders varying the permutations, but the success/permanent block rate remained the same
Because there is no stack trace and the randomness, I have no clue what causes the block. Except that using print statements all the server and client code the last prints that hang permanently is in the bytes receiving do-while loop in the server code
I am at a loss on what to do to solve this. I'm hoping experienced minds in this field would help solve this. Full code is below.
Java Server code
public class FileServer {
public static void main(String[] args) throws IOException {
int bytesRead;
int current = 0;
//===============================================
FileInputStream fis = null;
BufferedInputStream bis = null;
OutputStream os = null;
ServerSocket servsock = null;
Socket sock = null;
//==============================================
InetAddress IP=InetAddress.getLocalHost();
servsock = new ServerSocket(57925);
System.out.println("IP "+IP.getHostAddress()+" ***%% :"+servsock.getLocalPort());
while (true) {
sock = servsock.accept();
System.out.println("Accepted connection : " + sock);
InputStream is = sock.getInputStream();
//=========================================================
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
//=================== read integer from client ============
String number = br.readLine();
System.out.println("integer received from client is "+String.valueOf(number));
byte [] mybytearray = new byte [Integer.valueOf(number)];
//=================== read filename string =====================================
String filename = br.readLine();
System.out.println("integer received from client is "+filename);
//===================== read file data stream bytes ================================
bytesRead = is.read(mybytearray,0,mybytearray.length);
current = bytesRead;
System.out.println("1 bytesRead "+bytesRead+" mybytearray.length "+mybytearray.length);
do {
bytesRead = is.read(mybytearray, current, (mybytearray.length-current));
if(bytesRead >= 0) current += bytesRead;
System.out.println("2 current "+current+" bytesRead "+bytesRead);
} while(current < Integer.valueOf(number));
//============================== initialise filename ======================
FileOutputStream fos = new FileOutputStream("C:\\Server root folder\\"+filename+".jpg");
BufferedOutputStream bos = new BufferedOutputStream(fos);
//========================== write bytes to server HDD =======================
bos.write(mybytearray, 0 , current);
System.out.println("4 current "+current);
bos.flush();
long end = System.currentTimeMillis();
// System.out.println("AT SERVER: bytesRead "+bytesRead+" current "+current);
// bos.close();
// ======================== write to-be-rendered data to text file ======================
File pathPlusfile = new File("C:/Server root folder/"+filename+".txt");
appendToFile( pathPlusfile, "file name:: "+filename+"* *", 20999 );
/**/ //================== Send Data in text file to Client ============================================
// send file
mybytearray = new byte [(int)pathPlusfile.length()];
fis = new FileInputStream(pathPlusfile);
bis = new BufferedInputStream(fis);
bis.read(mybytearray,0,mybytearray.length);
//===============================================
os = sock.getOutputStream();
//=========================== send integer to client ===============
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
number = Integer.toString(mybytearray.length);
String sendMessage = number + "\n";
bw.write(sendMessage);
bw.flush();
//========================== send file to client ===================
System.out.println("Sending " + filename + "(" + mybytearray.length + " bytes)");
os.write(mybytearray,0,mybytearray.length);
os.flush();
//========================= close =================================
System.out.println("number "+number);
System.out.println("Done.");
bos.close();
bw.close();
osw.close();
os.close();
// fos.close();
// bis.close();
// fis.close();
// br.close();
isr.close();
is.close();
closeFile( );
// servsock.close();
// sock.close();
}
}
BufferedReader bufferedReader = null;
String stringObjectData = "";
public int numFromFile = 0;
static BufferedWriter bufferedWriter = null;
public static void appendToFile( File myPathPlusFile, String S, int num ){
try{
bufferedWriter = new BufferedWriter(new FileWriter(myPathPlusFile, true));
bufferedWriter.append( S );
bufferedWriter.append( " " );
bufferedWriter.append( Integer.toString(num) );
bufferedWriter.newLine();
bufferedWriter.flush();
}
catch (IOException e){
e.printStackTrace();
}
}
public static void closeFile( ){
try{
bufferedWriter.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
Android Client code
public class FSendfileActivity extends Activity {
private static final int SELECT_PICTURE = 1;
private Socket sock;
private String serverIP = "192.168.1.4";
private String selectedImagePath;
private ImageView img;
final static String qcd = "qcd";
String ImageDir2Client;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
Button send;
//====================
public static String FILE_TO_RECEIVED=null;
String cFilename = null;
int bytesRead = -1;
int current = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fsendfile);
ImageDir2Client = Environment.getExternalStorageDirectory().getAbsolutePath();
cFilename = "fromServer000019ggg";
FILE_TO_RECEIVED = ImageDir2Client + "/client root/"+cFilename+".txt";
img = (ImageView) findViewById(R.id.ivPic);
((Button) findViewById(R.id.bBrowse)).setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult( Intent.createChooser( intent, "Select Picture" ), SELECT_PICTURE );
}
});
send = (Button) findViewById(R.id.bSend);
send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
new Thread(new Runnable() {
#Override
public void run() {
try {
sock = new Socket();
connection(sock, serverIP, 57925);
//=================== prepare buffer to read file ====================
File myFile = new File (selectedImagePath);
byte [] mybytearray = new byte [(int)myFile.length()];
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
//=============== read file from sdcard to buffer ==========
bis.read(mybytearray,0,mybytearray.length);
//=================================================================
OutputStream os = sock.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
//============================= send size integer ===================
String number = Integer.toString(mybytearray.length);
String sendMessage = number + "\n";
bw.write(sendMessage); // send size integer here
//============================= send file name =====================
String sendMessage2 = cFilename + "\n";
bw.write(sendMessage2); // send size filename here
osw.flush();
bw.flush();
//==================================================================
os.write(mybytearray,0,mybytearray.length); // send file
os.flush();
//================= client receiving data ==============================
InputStream is = sock.getInputStream();
//=================== read integer from client ==========
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
number = br.readLine();
//========================= set incoming file size=============================
mybytearray = new byte [Integer.valueOf(number)];
//========================read file bytes in chunks===============================
bytesRead = is.read(mybytearray,0,mybytearray.length);
current = bytesRead;
do {
bytesRead = is.read(mybytearray, current, (mybytearray.length-current));
if(bytesRead >= 0) current += bytesRead;
} while(current < Integer.valueOf(number));
fos = new FileOutputStream(FILE_TO_RECEIVED);
bos = new BufferedOutputStream(fos);
bos.write(mybytearray, 0 , current);
bos.flush();
try{
bos.close();
osw.close();
os.close();
// fos.close();
// bw.close();
// br.close();
// isr.close();
bis.close();
sock.close();
// fis.close();
}
catch(Exception e){
e.printStackTrace();
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
try{
}
catch(Exception e){
e.printStackTrace();
}
}
}
}).start();
}
});
}
public static void connection(Socket s, String serverIP, int port) {
try {
Log.v(qcd, " before connecting ****...");
s.connect(new InetSocketAddress(serverIP, port), 120000);
Log.v(qcd, " socket connection DONE!! ");
} catch (UnknownHostException e) {
e.printStackTrace();
Log.v(qcd, " Unknown host..."+e);
} catch (IOException e) {
e.printStackTrace();
Log.v(qcd, " Failed to connect... "+e);
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == SELECT_PICTURE) {
img.setImageURI(null);
Uri selectedImageUri = data.getData();
selectedImagePath = getPath(selectedImageUri);
TextView path = (TextView) findViewById(R.id.tvPath);
path.setText("Image Path : " + selectedImagePath);
img.setImageURI(selectedImageUri);
}
}
}
public String getPath(Uri uri) {
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = managedQuery(uri, projection, null, null, null);
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
}
I finally fixed the code. I've had to test it over again all day long, to make sure it wasn't a false positive, so far its held on.
I hope this answer will help someone.
Originally I was using the same InputStream instance to read integer data, string data and file data at the server and the same outputStream instance to send integer data, string data and file data at the client. And at the client, they were wrapped in bufferOutputstream and bufferWritter and at server they were wrapped in bufferedInputstream and bufferedReader objects.
I think I was losing data at this point and thus the reason for the inconsistency
So to solve I had to create and use separate Outputstream instances for each data sent from the client. (note to send the file byte data - outputstream object wasn't wrapped)
Very subtle but solved it

Java HttpUrlConnection response code when connection closed by server

I am uploading file from android to server using HttpUrlConnection. In normal case file upload successful and correct HTTP response code (200) and message captured. But server also validates the file being uploaded and can close connection anytime while uploading which prohibits client to upload unnecessary data to server.
In brief, Server has following validation:
1. File MimeType check:
If file MimeType isn't any image MimeType then close connection immediately throwing HTTP response code 415 and message "Unsupported Media Type". This check done immediately after request arrived in server.
2. File size check:
While upload is being progressed as stream, if file size more then 5 MB then server close connection by throwing HTTP response code 413 and message "Request entity too large"
Current status:
If file is image type AND less then 5 MB upload successful.
If file is not image type OR more then 5 MB unable to get HTTP response code (413 or 415) and message from HttpUrlConnection object. SocketException or IOException occurs and then invoking connection.getResponseCode() throws another exception. Why not connection object holds response code and message sent from server ?
Code:
package com.example.mahbub.fileuploadrnd;
import android.util.Log;
import com.example.mahbub.fileuploadrnd.util.MimeTypesUtil;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.SocketException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class FileUploadRequest {
private static final String DEBUG_TAG = "FileUploadRequest";
public static final int ERROR_OUT_OF_MEMORY = 0x00;
public static final int ERROR_UNDEFINED = 0x01;
public static final int ERROR_HTTP_ERROR = 0x02;
/**
* The pool of ASCII chars to be used for generating a multipart boundary.
*/
private final static char[] MULTIPART_CHARS =
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static final String LINE_END = "\r\n";
private static final String TWO_HYPHENS = "--";
String url;
Map<String,String> mStringParts;
String filePartName;
String filePath;
UploadListener mListener;
public FileUploadRequest(String url, String filePartName, String filePath, UploadListener listener) {
this.url = url;
this.mStringParts = new HashMap<>();
this.filePartName = filePartName;
this.filePath = filePath;
this.mListener = listener;
}
public void execute() {
new Thread(new Runnable() {
#Override
public void run() {
executeRequest();
}
}).start();
}
public void addStringPart(String name, String value) {
this.mStringParts.put(name, value);
}
public void addStringParts(Map<String, String> parts) {
this.mStringParts.putAll(parts);
}
public interface UploadListener {
void onSuccess(int responseCode, String response);
void onError(int errorCode);
void transferred(long transferred, double progress);
}
private void executeRequest() {
HttpURLConnection connection = null;
DataOutputStream outputStream = null;
String boundary = generateBoundary();
try {
URL url = new URL(this.url);
connection = (HttpURLConnection) url.openConnection();
// Allow Inputs & Outputs
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setChunkedStreamingMode(4 * 1024);
// Enable POST method
connection.setRequestMethod("POST");
// Set header field value
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.writeBytes(TWO_HYPHENS + boundary + LINE_END);
// At first write all string parts
if(mStringParts.size() > 0) {
for (Map.Entry<String, String> entry : mStringParts.entrySet()) {
//entity.addTextBody(entry.getKey(), entry.getValue());
String partData = String.format("Content-Disposition: form-data; name=\"%s\"", entry.getKey()) + LINE_END + LINE_END;
partData += entry.getValue() + LINE_END;
partData += (TWO_HYPHENS + boundary + LINE_END);
outputStream.writeBytes(partData);
}
}
// Write file data
File file = new File(filePath);
FileInputStream fileInputStream = new FileInputStream(file);
String partData = String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", filePartName, file.getName()) + LINE_END;
partData += String.format("Content-Type: %s", getMimeType(file.getName()));
partData += LINE_END + LINE_END;
outputStream.writeBytes(partData);
// Input stream read buffer
byte[] buffer;
// Max possible buffer size
int maxBufferSize = 5 * 1024; // 5KB
// Bytes available to write in input stream
int bytesAvailable = fileInputStream.available();
Log.d(DEBUG_TAG, "File size: " + bytesAvailable);
// Buffer size
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
// Number of bytes read per read operation
int bytesRead;
// Allocate buffer
buffer = new byte[bufferSize];
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
boolean errorOccured = false;
try {
while(bytesRead > 0) {
try {
outputStream.write(buffer, 0, bufferSize);
} catch (OutOfMemoryError e) {
Log.d(DEBUG_TAG, "OutOfMemoryError occurred");
errorOccured = true;
e.printStackTrace();
if(null != mListener) mListener.onError(ERROR_OUT_OF_MEMORY);
break;
} catch (SocketException e) {
Log.d(DEBUG_TAG, "SocketException occurred");
errorOccured = true;
e.printStackTrace();
if(null != mListener) mListener.onError(ERROR_OUT_OF_MEMORY);
break;
} catch (IOException e) {
Log.d(DEBUG_TAG, "IOException occurred");
errorOccured = true;
e.printStackTrace();
if(null != mListener) mListener.onError(ERROR_OUT_OF_MEMORY);
break;
}
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
double progress = ((double)(file.length()-bytesAvailable) / file.length());
Log.d(DEBUG_TAG, "Progress: " + progress);
if(null != mListener) mListener.transferred(file.length()-bytesAvailable, progress);
}
} catch (Exception e) {
e.printStackTrace();
if(null != mListener) mListener.onError(ERROR_UNDEFINED);
}
if(!errorOccured) {
outputStream.writeBytes(LINE_END);
outputStream.writeBytes(TWO_HYPHENS + boundary + TWO_HYPHENS + LINE_END);
}
// Responses from the server (code and message)
int responseCode = connection.getResponseCode();
String response = connection.getResponseMessage();
Log.d(DEBUG_TAG, "Server Response Code: " + responseCode);
Log.d(DEBUG_TAG, "Server Response Message: " + response);
InputStream inStream = null;
if(responseCode >= 200 && responseCode < 400) inStream = connection.getInputStream();
else inStream = connection.getErrorStream();
String responseString = readStream(inStream);
Log.d(DEBUG_TAG, "responseString: " + responseString);
if(responseCode >= 200 && responseCode < 400) {
if(null != mListener) mListener.onSuccess(responseCode, responseString);
} else {
if(null != mListener) mListener.onError(ERROR_HTTP_ERROR);
}
fileInputStream.close();
outputStream.flush();
outputStream.close();
outputStream = null;
inStream.close();
} catch (Exception e) {
e.printStackTrace();
if(null != mListener) mListener.onError(ERROR_UNDEFINED);
}
}
private String readStream(InputStream iStream) throws IOException {
//build a Stream Reader, it can read char by char
InputStreamReader iStreamReader = new InputStreamReader(iStream);
//build a buffered Reader, so that i can read whole line at once
BufferedReader bReader = new BufferedReader(iStreamReader);
String line = null;
StringBuilder builder = new StringBuilder();
while((line = bReader.readLine()) != null) { //Read till end
builder.append(line);
}
bReader.close(); //close all opened stuff
iStreamReader.close();
//iStream.close(); //EDIT: Let the creator of the stream close it!
// some readers may auto close the inner stream
return builder.toString();
}
private String generateBoundary() {
final StringBuilder buffer = new StringBuilder();
final Random rand = new Random();
final int count = rand.nextInt(11) + 30; // a random size from 30 to 40
for (int i = 0; i < count; i++) {
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
}
return buffer.toString();
}
/**
* Get mimeType based on file extension
* #param fileName
* #return
*/
private String getMimeType(String fileName) {
String[] parts = fileName.split("\\.");
if(parts.length <= 1) return null;
return MimeTypesUtil.getMimeType(parts[parts.length-1]);
}
}

Categories

Resources