Can post files without having to base64 them.

This commit is contained in:
Pierre 2018-04-21 14:27:12 +02:00
parent 4c79e5de9f
commit d68755f4fe
7 changed files with 320 additions and 16 deletions

View File

@ -5,6 +5,9 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import org.communiquons.android.comunic.client.BuildConfig; import org.communiquons.android.comunic.client.BuildConfig;
import org.communiquons.android.comunic.client.data.models.APIFileRequest;
import org.communiquons.android.comunic.client.data.models.APIPostData;
import org.communiquons.android.comunic.client.data.models.APIPostFile;
import org.communiquons.android.comunic.client.data.models.APIRequest; import org.communiquons.android.comunic.client.data.models.APIRequest;
import org.communiquons.android.comunic.client.data.models.APIResponse; import org.communiquons.android.comunic.client.data.models.APIResponse;
@ -16,8 +19,11 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@ -115,6 +121,126 @@ public class APIRequestHelper {
return result; return result;
} }
/**
* Execute an API request over the server
*
* Note : this methods is based on a StackOverflow answer:
* https://stackoverflow.com/a/33149413/3781411
*
* @param req Information about the request
* @return The response of the server
* @throws Exception In case of failure during the connection with the API
*/
public APIResponse execPostFile(APIFileRequest req) throws Exception {
//Add API and login tokens to the request
addAPItokens(req);
addLoginTokens(req);
//Prepare response
APIResponse response = new APIResponse();
HttpURLConnection conn = null;
OutputStream out;
PrintWriter writer;
//Create unique boundary
String boundary = "===" + System.currentTimeMillis() + "===";
String LINE_FEED = "\r\n";
try {
//Initialize connection
URL url = new URL(BuildConfig.api_url + req.getRequest_uri());
conn = (HttpURLConnection) url.openConnection();
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
//Get output stream
out = conn.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
//Append values
for(APIPostData value : req.getParameters()){
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"" + value.getEncodedKeyName() + "\"")
.append(LINE_FEED);
writer.append("Content-Type: form-data; charset=UTF-8").append(
LINE_FEED);
writer.append(LINE_FEED);
writer.append(value.getKey_value()).append(LINE_FEED);
writer.flush();
}
//Append files
for(APIPostFile file : req.getFiles()){
String fileName = file.getFileName();
writer.append("--" + boundary).append(LINE_FEED);
writer.append(
"Content-Disposition: form-data; name=\"" + file.getFieldName()
+ "\"; 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();
out.write(file.getByteArray());
out.flush();
writer.append(LINE_FEED);
writer.flush();
}
//Finish request and get response
writer.append(LINE_FEED).flush();
writer.append("--" + boundary + "--").append(LINE_FEED);
writer.close();
StringBuilder responseBuffer = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
responseBuffer.append(line);
}
reader.close();
conn.disconnect();
//Return the response
response.setResponse_code(conn.getResponseCode());
response.setResponse(responseBuffer.toString());
}
//Malformed URL Exceptions must be fixed by dev
catch (MalformedURLException e) {
e.printStackTrace();
throw new RuntimeException("MalformedURLException should never occur...");
}
catch (IOException e) {
e.printStackTrace();
response.setResponse_code(0);
if(req.isTryContinueOnError() && conn != null){
response.setResponse_code(conn.getResponseCode());
return response;
}
//Throw an exception
throw new Exception("Could not connect to the server");
}
return response;
}
// Reads an InputStream and converts it to a String. // Reads an InputStream and converts it to a String.
private String readIt(InputStream stream) throws IOException { private String readIt(InputStream stream) throws IOException {
@ -133,7 +259,7 @@ public class APIRequestHelper {
/** /**
* Add the API client tokens to API request object * Add the API client tokens to API request object
* *
* @param params The request parametres to update * @param params The request parameters to update
*/ */
private void addAPItokens(APIRequest params){ private void addAPItokens(APIRequest params){
params.addString("serviceName", BuildConfig.api_service_name); params.addString("serviceName", BuildConfig.api_service_name);

View File

@ -1,9 +1,13 @@
package org.communiquons.android.comunic.client.data.helpers; package org.communiquons.android.comunic.client.data.helpers;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.communiquons.android.comunic.client.data.models.APIFileRequest;
import org.communiquons.android.comunic.client.data.models.APIPostFile;
import org.communiquons.android.comunic.client.data.models.APIRequest; import org.communiquons.android.comunic.client.data.models.APIRequest;
import org.communiquons.android.comunic.client.data.models.APIResponse; import org.communiquons.android.comunic.client.data.models.APIResponse;
import org.communiquons.android.comunic.client.data.models.ConversationMessage; import org.communiquons.android.comunic.client.data.models.ConversationMessage;
@ -82,23 +86,36 @@ public class ConversationMessagesHelper {
* *
* @param convID Target conversation ID * @param convID Target conversation ID
* @param message The message to send * @param message The message to send
* @param image Base64 encoded image to include with the message (can be null) * @param image Image to include with the request, as bitmap (can be null)
* @return true in case of success / false else * @return true in case of success / false else
*/ */
public boolean sendMessage(int convID, String message, @Nullable String image){ public boolean sendMessage(int convID, String message, @Nullable Bitmap image){
//Make an API request //Make an API request
APIRequest params = new APIRequest(mContext, APIFileRequest params = new APIFileRequest(mContext,
"conversations/sendMessage"); "conversations/sendMessage");
params.addString("conversationID", ""+convID); params.addString("conversationID", ""+convID);
params.addString("message", message); params.addString("message", message);
//Include image (if any) //Include image (if any)
if(image != null) if(image != null) {
params.addString("image", "data:image/png;base64," + image); APIPostFile file = new APIPostFile();
file.setFieldName("image");
file.setFileName("conversationImage.png");
file.setBitmap(image);
params.addFile(file);
}
try { try {
if(image != null){
//Perform a POST request
new APIRequestHelper().execPostFile(params);
}
else
//Perform normal request
new APIRequestHelper().exec(params); new APIRequestHelper().exec(params);
return true; return true;
} catch (Exception e){ } catch (Exception e){
e.printStackTrace(); e.printStackTrace();

View File

@ -0,0 +1,51 @@
package org.communiquons.android.comunic.client.data.models;
import android.content.Context;
import java.util.ArrayList;
/**
* This class handles information about the request that includes files to the server
*
* @author Pierre HUBERT
* Created by pierre on 4/21/18.
*/
public class APIFileRequest extends APIRequest {
/**
* The list of post files
*/
private ArrayList<APIPostFile> files;
/**
* The class constructor
*
* @param context The context of the request
* @param uri The request URI on the server
*/
public APIFileRequest(Context context, String uri) {
super(context, uri);
//Create files list
files = new ArrayList<>();
}
/**
* Add a file to the request
*
* @param file The file to add
*/
public void addFile(APIPostFile file){
files.add(file);
}
/**
* Get the list of files
*
* @return The list of the files to include into the request
*/
public ArrayList<APIPostFile> getFiles() {
return files;
}
}

View File

@ -9,7 +9,7 @@ import java.net.URLEncoder;
* Created by pierre on 10/31/17. * Created by pierre on 10/31/17.
*/ */
class APIPostData { public class APIPostData {
/** /**
* The name of the key * The name of the key
@ -32,6 +32,52 @@ class APIPostData {
key_value = value; key_value = value;
} }
/**
* Get the name of the key
*
* @return The name of key
*/
public String getKey_name() {
return key_name;
}
/**
* Get the value associated with the ky
*
* @return The value of the key
*/
public String getKey_value() {
return key_value;
}
/**
* Get the key name, as an encoded string
*
* @return The encoded key name
*/
public String getEncodedKeyName(){
try {
return URLEncoder.encode(getKey_name(), "UTF-8");
} catch (java.io.UnsupportedEncodingException e){
e.printStackTrace();
throw new RuntimeException("Unsupported encoding : UTF-8 !", e);
}
}
/**
* Get the key value, as an encoded string
*
* @return The encoded key value
*/
public String getEncodedKeyValue(){
try {
return URLEncoder.encode(getKey_value(), "UTF-8");
} catch (java.io.UnsupportedEncodingException e){
e.printStackTrace();
throw new RuntimeException("Unsupported encoding : UTF-8 !", e);
}
}
/** /**
* Get the the name and the value of the key in an encoded form * Get the the name and the value of the key in an encoded form
* *

View File

@ -0,0 +1,61 @@
package org.communiquons.android.comunic.client.data.models;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import java.io.ByteArrayOutputStream;
/**
* Single file information included in a request to the API
*
* @author Pierre HUBERT
* Created by pierre on 4/21/18.
*/
public class APIPostFile {
//Private fields
private String fieldName;
private String fileName;
private byte[] byteArray;
//Set and get field name
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getFieldName() {
return fieldName;
}
//Set and get file name
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
//Set and get byte array
public void setByteArray(byte[] byteArray) {
this.byteArray = byteArray;
}
public byte[] getByteArray() {
return byteArray;
}
/**
* Set a bitmap as the file
*
* @param bmp Bitmap to set
*/
public void setBitmap(@NonNull Bitmap bmp){
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
setByteArray(stream.toByteArray());
}
}

View File

@ -112,6 +112,15 @@ public class APIRequest {
return result; return result;
} }
/**
* Get the list of parameters
*
* @return The list of parameters
*/
public ArrayList<APIPostData> getParameters() {
return parameters;
}
/** /**
* Get the context of the request * Get the context of the request
* *

View File

@ -535,14 +535,8 @@ public class ConversationFragment extends Fragment
@Override @Override
protected Boolean doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
String message_image = null; return convMessHelper.sendMessage(conversation_id,
message_content, new_message_bitmap);
//Reduce Bitmap and convert it to a base64-encoded string
if(new_message_bitmap != null)
message_image = BitmapUtils.bitmapToBase64(
BitmapUtils.reduceBitmap(new_message_bitmap, 1199, 1199));
return convMessHelper.sendMessage(conversation_id, message_content, message_image);
} }
@Override @Override