What are the possible ways/libraries available to upload images from android app to sails.js Node.js server?
One way that I came across to achieve this is sending Base64 encoded Bitmap image string from the app and saving it to the database, but this seems to be an inefficient way to handle multiple big size images as Base64 encoded string is 33% larger than raw size.
Another way is to send images as multipart form data, but I couldn't found good examples on this. Please, provide examples which demonstrate how to send images from app and handle it at serverside (node.js/sails.js)
Are there any other recommended libraries available to handle image uploading in android?
I use Multer to handle file uploads via multipart form data.
Out of the box it can do memory and disk storage. By using plugin modules you can use Multer to send files direct to S3 or other storage providers.
For backend level, use this piece of code in your SailsJS application:
uploadPhoto: function (req, res) {
req.file('photo').upload({
adapter: require('skipper-s3'),
key: S3_KEY,
secret: S3_SECRET,
bucket: IMAGE_BUCKET_NAME,
dirname: DIRECTORY_NAME,
region: S3_REGION
}, function (err, uploaded) {
if(err) {
//Image not uploaded
//Returned with error
} else if(uploaded.length == 0) {
//Image not uploaded
} else {
//Image uploaded
//Returned Image Path
var imagePath = uploaded[0].extra.Location;
}
});
},
And you need to send the file using multipart request. I am doing it with retrofit library. Here is the android code:
1. Create Multipart RequestBody Object
RequestBody file =
RequestBody.create(MediaType.parse("multipart/form-data"), photo); //photo is of type "File"
2. Handling RequestBody in Interface
#Multipart
#POST("api/uploadphoto")
Call<ResponseBody> uploadPhoto(#Part("photo\"; filename=\"pp\"") RequestBody file);
3. Then initiating the call to server
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {
Log.e("RESPONSE", response.body());
}
}
#Override
public void onFailure(Throwable t) {
Log.e("UploadPhoto", "failed");
}
});
That will upload the image to S3-Bucket.
Sails.js Backend file upload code and file will be uploaded in assets/images folder
upload: function (req, res) {
if (req.method === 'GET')
return res.json({
'status': 'GET not allowed'
});
// Call to /upload via GET is error
var data = req.file('uploadFile');
data.upload({
dirname: '../../assets/images'
}, function onUploadComplete(err, files) {
// Earlier it was ./assets/images .. Changed to ../../assets/images
// Files will be uploaded to ./assets/images
// Access it via localhost:1337/images/file-name
console.log(files);
if (err) {
console.log(err);
return res.json(500, err);
} else if (files.length === 0) {
// proceed without files
res.notFound({
status: 'Unsucess',
response: 'File not Uploaded'
});
} else {
// handle uploaded file
res.json({
status: 200,
file: files
});
}
});
}
Android Code :-
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("fileUploadType", "1")
.addFormDataPart("miniType", contentType)
.addFormDataPart("ext", file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf(".")))
.addFormDataPart("fileTypeName", "img")
.addFormDataPart("clientFilePath", selectedImageUri.getPath())
.addFormDataPart("filedata", filename + ".png", fileBody)
.build();
Request request = new Request.Builder()
.url(API_URL)
.post(requestBody)
.build();
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, final IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
et_response.setText(e.getMessage());
Toast.makeText(MainActivity.this, "nah", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
et_response.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(MainActivity.this, "response: " + response, Toast.LENGTH_LONG).show();
}
});
}
});
For Android Code You can Refer to
http://blog.aimanbaharum.com/2016/03/26/android-image-multi-part-upload/
Related
I have a File and i want to send it to Spring Backend from Android. After receiving the image at Spring I am changing the name of the Image by Generating the UUID and then uploading it to AWS S3. My problem is i am getting null value as response.
Android Side ->
My Android Upload File Function ->
private void UploadFiles() {
File uploadFile = fileArrayList.get(0);
if (uploadFile != null) {
Log.d(TAG, "UploadFiles: File Name is -> " + uploadFile.getName());
// cropImageRequest.setCropId(uploadFile.getParentFile().getName());
// Parsing any Media type file
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), uploadFile);
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part cropImage = MultipartBody.Part.createFormData("cropImage", uploadFile.getName(), requestFile);
Api.uploadCropImage(cropImage, new Callback<BasicResponse>() {
#Override
public void onResponse(Call<BasicResponse> call, Response<BasicResponse> response) {
if (response.body() != null) {
Log.d(TAG, "onResponse: Success" + response.body().getResponse());
}
else{
Log.d(TAG, "onResponse: null Response");
}
}
#Override
public void onFailure(Call<BasicResponse> call, Throwable t) {
Log.d(TAG, "onResponse: Failure");
}
});
}
}
**uploadImageFunction -> **
public static void uploadCropImage(MultipartBody.Part cropImage, Callback<BasicResponse> callback) {
UploadCropImageApi uploadCropImageApi = retrofit.create(UploadCropImageApi.class);
Call<BasicResponse> call = uploadCropImageApi.uploadCropImage(cropImage);
call.enqueue(callback);
}
My Interface ->
public interface UploadCropImageApi {
#Multipart
#POST(UPLOAD_FILE_TO_AWS_URL)
Call<BasicResponse> uploadCropImage(#Part MultipartBody.Part cropImage);
}
This is my Spring File ->
#RequestMapping(value = "/UploadCropImage", method = RequestMethod.POST, consumes = {"multipart/form-data"})
#ResponseBody
public String UploadImage(#RequestBody MultipartFile cropImage,HttpServletRequest request) {
mAmazonClient = AmazonClient.GetAmazonClientInstance();
UUIDUtils uuid = new UUIDUtils();
try {
System.out.println(cropImage);
String KeyName = uuid.GenerateUUID(cropImage.getName());
String Code = mAmazonClient.uploadImage(KeyName, cropImage);
System.out.println(Code);
return Code;
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
return null;
}
}
This Controller is printing following value ->
org.springframework.web.multipart.commons.CommonsMultipartFile#b0b5de0
File Name is -: cropImage
null
My Problem is that as you can see the file sent by Retrofit and received by Spring is not null, I am sending that file via AWS, but it's not uploading the file and returns null as value. But when i use POSTMAN it's easily sending the file to AWS and returns the KeyName.
Okay so i debug a little bit and found out an exception while uploading Image file. and the exception is this -> The filename, directory name, or volume label syntax is incorrect.
Your exception says there is something wrong in your file name. The problem is naming convention as it was uploading file name containing ":" sign and there may be other signs as well which is not accepted by AWS. so, just change file name and separate the names by _(Underscore) sign instead of " "(space), (:) and other signs. This will successfully upload the file to the AWS. Hope it helps.
I have an app that posting file with some string. I used retrofit 2. Everything is OK. but I have a problem. I run app in emulator an it works fine and goes to onResponse and post file and strings, but when I run app in real device it doesn't work and goes to onFailure in retrofit. What is the problem. I use galaxy s6 edge with android 7 API 24. and emulator is android 7.1.1 API 25. please help I should fix this problem :(
this is my retrofit code:
if (name.getText().toString().equals("") ||
description.getText().toString().equals("") ||
deadline.getText().toString().equals("")) {
popupForErrorEmptyField();
} else {
Client client = ServiceGenerator.createService(Client.class);
File file = null;
RequestBody requestFile = null;
MultipartBody.Part body = null;
if (userProfileImagePath != null) {
try {
file = new File(userProfileImagePath);
Uri uri = Uri.fromFile(file);
if (file.exists()) {
Log.i("finaltest", "found");
requestFile =
RequestBody.create(MediaType.parse(getMimeType(uri)), file);
body =
MultipartBody.Part.createFormData("projectFile", file.getName(), requestFile);
} else {
Log.i("finaltest", "not found");
}
} catch (Exception e) {
}
}
RequestBody nameRequestBody = RequestBody.create(MediaType.parse("text/plain"), name.getText().toString().trim());
RequestBody amountRequestBody = RequestBody.create(MediaType.parse("text/plain"), amount.getText().toString().trim());
RequestBody descriptionRequestBody = RequestBody.create(MediaType.parse("text/plain"), description.getText().toString().trim());
RequestBody categoryRequestBody = RequestBody.create(MediaType.parse("text/plain"), category.getSelectedItem().toString());
RequestBody deadlineRequestBody = RequestBody.create(MediaType.parse("text/plain"), deadline.getText().toString().trim());
Call<AddProjectResponse> call = client.addProject(
token,
body,
nameRequestBody,
amountRequestBody,
descriptionRequestBody,
categoryRequestBody,
deadlineRequestBody);
call.enqueue(new Callback<AddProjectResponse>() {
#Override
public void onResponse(Call<AddProjectResponse> call,
Response<AddProjectResponse> response) {
Log.e("xcxc", "code:" + response.code());
if (response.isSuccessful()) {
Log.e("xcxc", "success:");
popupForSuccessAddingProject();
} else {
Toast.makeText(getContext(), "successfull!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<AddProjectResponse> call, Throwable t) {
Log.e("test123", "error in add project");
}
});
}
I am not sure this will solve everyone's problems, in my case it's one filed was expired which got the server run into crash, since it's working on the emulator and not on physical device do the following:
get Postman to use it in testing.
use the retrofit logger interceptor to collect the logs and get the all values of the request from both physical device and emulator
try the working request body on postman(in this case the emulator)
try to replace each field of the working request body(emulator) in postman with it's corresponding one from the not working request body (real device) and test the request at each change until one of them causes an error.
in my case the userId was expired and this cause an error from the server
I am new to retrofit and I am trying to send a comment to a specific media using retrofit and the Instagram API.
The Instagram API tells me that my request must be:
curl -F 'access_token=ACCESS-TOKEN'
-F 'text=This+is+my+comment'
https://api.instagram.com/v1/media/{media-id}/comments
and the JSON response is :
{
"meta":
{
"code": 200
},
"data": null
}
So I made this retrofit grammar:
#FormUrlEncoded
#POST("v1/media/{media_id}/comments")
Call<Object> postComment(
#Path("media_id") String mediaId,
#Field("access_token") String accessToken,
#Field("text") String text);
My Retrofit Service:
public class RestClient
{
public static RetrofitInstagram getRetrofitService()
{
return new Retrofit.Builder()
.baseUrl(Constants.AUTH_URL)
.addConverterFactory(GsonConverterFactory.create())
.build().create(RetrofitInstagram.class);
}
}
My call (inside an AlertDialog get the text from an EditText) is :
Call<Object> call = RestClient.getRetrofitService().postComment(data.get(idx).getId(), access_token, titleEditText.getText().toString());
call.enqueue(new Callback<Object>()
{
#Override
public void onResponse(Call<Object> call, Response<Object> response)
{
Log.d("response comment", ""+response.raw());
Toast.makeText(activity_instagram_feed_search.this, "Comments sent", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<Object> call, Throwable t)
{
Toast.makeText(activity_instagram_feed_search.this, "error", Toast.LENGTH_SHORT).show();
}
});
My problem here is that I am receiving a code 400 error (Missing client_id or access_token URL parameter.).
It's like the api think I am doing a GET request.
I am really confuse, I would appreciate some wisdom :).
I managed to find the solution to my question.
So Ben P was wrong and the Curl -F is x-www-form-urlencoded.
I am in sandbox account mode and forgot to add comments scope to my loggin
WebView request.
So my WebView url is now:
private final String url = Constants.AUTH_URL
+ "oauth/authorize/?client_id="
+ Constants.CLIENT_ID
+ "&redirect_uri="
+ Constants.REDIRECT_URI
+ "&response_type=token"
+ "&display=touch&scope=public_content+comments";
How can I send HTTP GET and POST requests in C# with Unity?
What I want is:
send json data in post request (I use Unity serializer, so no need in
new one, I just want to pass string in post data and have ability to
set ContentType to application/json);
get response code and body without any problems;
do it all asynchronous without blocking ui rendering.
What I've tried:
implementing with HttpWebRequest/HttpWebResponse, but it's too hard and low level (if I won't found anything better, I'll have to use it);
using unity WWW, but it doesn't match my requirements;
using some external packages from NuGet - Unity don't accept them :(
Most problems were with threading, I'm not experienced enough in it in C#.
IDE, I use, is Intellij Rider.
The WWW API should get this done but UnityWebRequest replaced it so I will answer the newer API. It's really simple. You have to use coroutine to do this with Unity's API otherwise you have have to use one of C# standard web request API and Thread. With coroutine you can yield the request until it is done. This will not block the main Thread or prevent other scripts from running.
Note:
For the examples below, if you are using anything below Unity 2017.2, replace SendWebRequest() with Send() and then replace isNetworkError with isError. This will then work for the lower version of Unity. Also, if you need to access the downloaded data in a binary form instead, replace uwr.downloadHandler.text with uwr.downloadHandler.data. Finally, the SetRequestHeader function is used to set the header of the request.
GET request:
void Start()
{
StartCoroutine(getRequest("http:///www.yoururl.com"));
}
IEnumerator getRequest(string uri)
{
UnityWebRequest uwr = UnityWebRequest.Get(uri);
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Received: " + uwr.downloadHandler.text);
}
}
POST request with Form:
void Start()
{
StartCoroutine(postRequest("http:///www.yoururl.com"));
}
IEnumerator postRequest(string url)
{
WWWForm form = new WWWForm();
form.AddField("myField", "myData");
form.AddField("Game Name", "Mario Kart");
UnityWebRequest uwr = UnityWebRequest.Post(url, form);
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Received: " + uwr.downloadHandler.text);
}
}
POST request with Json:
void Start()
{
StartCoroutine(postRequest("http:///www.yoururl.com", "your json"));
}
IEnumerator postRequest(string url, string json)
{
var uwr = new UnityWebRequest(url, "POST");
byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(json);
uwr.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
uwr.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
uwr.SetRequestHeader("Content-Type", "application/json");
//Send the request then wait here until it returns
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Received: " + uwr.downloadHandler.text);
}
}
POST request with Multipart FormData/Multipart Form File:
void Start()
{
StartCoroutine(postRequest("http:///www.yoururl.com"));
}
IEnumerator postRequest(string url)
{
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add(new MultipartFormDataSection("field1=foo&field2=bar"));
formData.Add(new MultipartFormFileSection("my file data", "myfile.txt"));
UnityWebRequest uwr = UnityWebRequest.Post(url, formData);
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Received: " + uwr.downloadHandler.text);
}
}
PUT request:
void Start()
{
StartCoroutine(putRequest("http:///www.yoururl.com"));
}
IEnumerator putRequest(string url)
{
byte[] dataToPut = System.Text.Encoding.UTF8.GetBytes("Hello, This is a test");
UnityWebRequest uwr = UnityWebRequest.Put(url, dataToPut);
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Received: " + uwr.downloadHandler.text);
}
}
DELETE request:
void Start()
{
StartCoroutine(deleteRequest("http:///www.yoururl.com"));
}
IEnumerator deleteRequest(string url)
{
UnityWebRequest uwr = UnityWebRequest.Delete(url);
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Deleted");
}
}
Use HttpClient and something like:
public static HttpContent DoPost(object payload, string subPath)
{
var httpClient = new HttpClient();
HttpClient.BaseAddress = new Uri(Global.BaseUrl);
HttpClient.DefaultRequestHeaders.Clear();
HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // if you're using json service
// make request
var response = Global.HttpClient.PostAsJsonAsync(subPath.TrimLeadingSlash(), payload).Result;
// check for error
response.EnsureSuccessStatusCode();
// return result
return response.Content;
}
Payload is an object to be serialized to json. If all requests are going to the same baseUrl, you can set up HttpClient globally, and reuse it here
https://www.patrykgalach.com/2019/04/18/how-to-call-rest-api-in-unity/
Please refer this link cleanest way to play with data
and do not use www instead of use UnityWebRequest
We can use WWW and UnityWebRequest classes to initiate API calls. WWW got obsolete now and Unity recommends using UnityWebRequest over WWW.
void Start() {
string url = "https://retrofit-backend-demo.herokuapp.com/book";
StartCoroutine(GetBooksUsingWWW(url));
StartCoroutine(GetBooksUsingUnityWebRequest(url));
}
IEnumerator GetBooksUsingWWW(string url) {
using (WWW www = new WWW(url)){
yield return www;
Debug.Log(www.text);
JSONNode jsonNode = JSON.Parse(www.text);
string title = jsonNode[0]["title"].ToString();
Debug.Log("Title: " + title);
}
}
IEnumerator GetBooksUsingUnityWebRequest(string url) {
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
Debug.Log(www.downloadHandler.text);
JSONNode jsonNode = JSON.Parse(www.downloadHandler.text);
string title = jsonNode[0]["title"].ToString();
Debug.Log("Title: " + title);
}
}
For demo: https://github.com/codemaker2015/api-interaction-unity3d-demo
I am using Retrofit library to upload media files (multipart) from my Android application. The servers are on Amazon using S3.
I am getting this following error :
05-15 20:17:38.515: W/System.err(649): Caused by: javax.net.ssl.SSLException: Write error: ssl=0x5eed7ca0: I/O error during system call, Broken pipe
Few points :
1. I have tested the API using POSTMAN and it is working perfectly. (no issues with max upload size as well.)
2. Weirdly this is running(uploading) successfully in one of my phones ie Moto E. The phone it is not working includes Moto G2 and Xperia SP as of now.
3. I am able to make normal requests through Retrofit successfully. Its uploading media files that is an issue.
Here is the code to upload :
#Multipart
#POST("/journeys/{journey_id}/videos")
public void uploadVideo(
#Path("journey_id") String journeyId,
#Part("api_key") TypedString apiKey,
#Part("video[user_id]") TypedString userId,
#Part("video[video_file]") TypedFile video,
Callback<String> callback);
public static void uploadVideo(final Context context, final Video video) {
RestAdapter restAdapter = new RestAdapter.Builder().setConverter(new StringConverter())
.setEndpoint(Constants.TRAVELJAR_API_BASE_URL).build();
TravelJarServices myService = restAdapter.create(TravelJarServices.class);
myService.uploadVideo(TJPreferences.getActiveJourneyId(context), new TypedString(
TJPreferences.getApiKey(context)),
new TypedString(TJPreferences.getUserId(context)), new TypedFile("video/*",
new File(video.getDataLocalURL())), new Callback<String>() {
#Override
public void success(String str, retrofit.client.Response response) {
try {
Log.d(TAG, "video uploaded successfully " + str);
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void failure(RetrofitError retrofitError) {
Log.d(TAG, "error in uploading video" + retrofitError);
retrofitError.printStackTrace();
}
});
}
I have been researching on this issue for a while now and cannot come to a solution. I don't want to switch to another library as this should work for me as well. Any help will be highly appreciated. TIA