Okhttp: How to add multi-part form part using a loop? - android

I am using OKHttp to send multipart data. Here a piece is my code:
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
bodyBuilder.setType(MultipartBody.FORM)
.addFormDataPart("arrival_to", object.optString("arrival_to"))
.addFormDataPart("centre_id", object.optString("centre_id"))
.addFormDataPart("departure_from", object.optString("departure_from"))
.addFormDataPart("expense_item_id", object.optString("expense_item_id"))
.addFormDataPart("expense_sub_item_id", object.getString("expense_sub_item_id"))
.addFormDataPart("file_list", object.optString("file_list"))
.addFormDataPart("journey_date", object.optString("journey_date"))
.addFormDataPart("passenger_age", object.optString("passenger_age"))
.addFormDataPart("passenger_gender", object.optString("passenger_gender"))
.addFormDataPart("passenger_name", object.optString("passenger_name"))
.addFormDataPart("personal_id", object.optString("personal_id"))
.addFormDataPart("remarks", object.optString("remarks"))
.addFormDataPart("request_amount", object.optString("request_amount"))
.addFormDataPart("request_status", object.optString("request_status"))
.addFormDataPart("travel_class", object.optString("travel_class"))
.addFormDataPart("vehicle_name_no", object.optString("vehicle_name_no"))
.addFormDataPart("vendor_id", object.optString("vendor_id"))
.addFormDataPart("vendor_type_id", object.optString("vendor_type_id"))
.addFormDataPart("within_budget", object.optString("within_budget"))
.addFormDataPart("file_list", attach_file, RequestBody.create(MediaType.parse(type), file))
.build();
The problem here is that I am adding the form data parts one by one which is ok here but I wanted to use a loop to add the form data part in case the dataset increased. Also I am making a seperate class containing the code to upload the data, how do I pass the key value pairs to that class should I use a Map?

Maybe something like this works?
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
MultipartBody.Builder builder = bodyBuilder.setType(MultipartBody.FORM);
String[] keys = new String[]{
"arrival_to",
"centre_id",
"departure_from",
"expense_item_id",
"...."
};
Arrays.stream(keys)
.forEach(key -> {
builder.addFormDataPart(key, object.optString(key));
});
}

Related

Xamarin Forms, Android and HttpClient issue

I've created in Xamarin Forms for iOS a HttpClient function to send a picture from the device to my server. The core function is
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(fileBytes);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fName
};
fileContent.Headers.ContentDisposition.Parameters
.Add(new NameValueHeaderValue("userId", UserId.ToString()));
content.Add(fileContent);
using (var client = new HttpClient()) {
client.DefaultRequestHeaders.Add("authenticationToken", SyncData.Token);
HttpResponseMessage response = await client.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
// more code
}
}
I'm using System.Net.Http. I tried to use the same function for a project in Android but surprisingly it doesn't work. The problem is in the header: if I inspect fileContent I can see every keys but for webapi on the server FileName is not received.
After some logs, I changed this function adding more client.DefaultRequestHeaders like
client.DefaultRequestHeaders.Add("FileName", fName);
Now the webapi receives FileName param.
Now my question is: what did I wrong?
Personally, I use the Add method on MultipartFormDataContent that accepts a filename.
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(fileBytes);
...
// Use the overload Add method which accepts a file name
content.Add(fileContent, "FileName", fName);
...
I'm not sure if this will solve your problem or not, but it works for me.

Save image in Django from Android app

I was having problem while saving picture in Django from Android app. I searched and finally solved the problem. I am sharing this so that it might help. Please see the answer below.
You will have to implement things according to your own specifications. I am just showing you as a generic example
I have used okHttp in my android app to send data on network (including pic)
Android AsyncTask Code (doInBackground Method)
RequestBody formBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("filename","filename",RequestBody.create(MediaType.parse("multipart/form-data"), new File(file.getPath())))
//.addFormDataPart("other_field", "other_field_value")
.build();
Request request = new Request.Builder()
.header("Authorization", "Token " + myToken)
.url(myUrl).post(formBody).build();
Response response = new OkHttpClient().newCall(request).execute();
return response.body().string();
My view.py Code
def rechargeapplication(request):
user=#get your own object
uploadpic = request.FILES['filename']
user.picture.save("image.jpg",uploadpic)
user.save()
return JsonResponse({'result':'Success'})
How I created imageField in models.py
picture=models.ImageField(upload_to="photos" , null=True, blank=True)
if you are using ImageField then you will have to install "Pillow"
Also make sure that you specify MEDIA_ROOT & MEDIA_URL in settings.py....I am showing you how I did it
MEDIA_ROOT=os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
and at the end of urls.py add this(as I was in debug mode that is why my implementation...)
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

How do I add "sheetId": XXXXXX to my Google Sheets v4 function batchupdate?

I'm almost finished with my application where I'm able to do a simple delete off of Google Spraedsheet data. However, I have not been able to find a method where I could add the sheetId and its respective GID # to the request arraylist.
private void deleteRow()
{
List<Request> requests = new ArrayList<>();
DeleteDimensionRequest deleteDimensionRequest = new DeleteDimensionRequest();
DimensionRange dimensionRange = new DimensionRange();
dimensionRange.getDimension();
dimensionRange.setStartIndex(13);
dimensionRange.setEndIndex(14);
deleteDimensionRequest.setRange(dimensionRange);
Sheets.Spreadsheets spreadsheets = null;
requests.add(new Request()
//There should be a function call or some sort for me to
//add a sheetid... if I do the updatesheets property here
//I get an error message saying that there's already a kind
//and I cannot set the id
.setDeleteDimension(deleteDimensionRequest)
);
BatchUpdateSpreadsheetRequest batchUpdateRequest = new BatchUpdateSpreadsheetRequest()
.setRequests(requests);
try
{
mService.spreadsheets().batchUpdate("SPREADSHEETID GOES HERE", batchUpdateRequest).execute();
}
catch(IOException e)
{
e.printStackTrace();
}
}
Does anyone know the strategy to add the sheet values into the request arraylist?
The function calls were actually available after creating a new constructor for DimensionRange.
Simply do:
dimensionRange.setDimension("ROWS");
dimensionRange.setSheetId(XXXXX);
to finish the JSON post request to Sheets API...

How to insert row after the last row with value?

I am inserting data into a spreadsheet with the new Google Sheets API v4, the code works perfect and the data it is inserted well in the sheet.
But how to find out the last row with data to add the data after this ?
List<List<Object>> arrData = getData();
ValueRange oRange = new ValueRange();
oRange.setRange("Pedidos!AXXXXXXX"); // I NEED THE NUMBER OF THE LAST ROW
oRange.setValues(arrData);
List<ValueRange> oList = new ArrayList<>();
oList.add(oRange);
BatchUpdateValuesRequest oRequest = new BatchUpdateValuesRequest();
oRequest.setValueInputOption("RAW");
oRequest.setData(oList);
BatchUpdateValuesResponse oResp1 = mService.spreadsheets().values().batchUpdate("ID_SPREADSHEET", oRequest).execute();
Is there some trick in the A1 notation for this?
I need an equivalent to .getLastRow from Google Apps Script.
If you use the append feature and set the range to the entire sheet, the API will find the last row and append the new data after it.
This web page explains it.
https://developers.google.com/sheets/api/guides/values#appending_values
Here is some sample code:
String range="Sheet1";
insertData.setValues(rows);
AppendValuesResponse response=service.spreadsheets().values()
.append(spreadsheetId,range,insertData).setValueInputOption("USER_ENTERED")
.execute();
Note that the response will tell you where it was inserted.
The v4 API has no way to ask "what is the last row with data", as it's a different style of API than the Apps Script API. You can infer the last row yourself by requesting the data and counting the offset from your first requested row to the last returned row.
You can use AppendCellsRequest to append a row. The below methods should get you going. I haven't included the getRowDataListForCellStrings method as it is rather application specific.
First create a Request object containing a AppendCellsRequest:
public BatchUpdateSpreadsheetResponse appendWorksheet(String cellValues) throws SpreadsheetException {
AppendCellsRequest appendRequest = new AppendCellsRequest();
appendRequest.setSheetId( mSheet.getProperties().getSheetId() );
appendRequest.setRows( getRowDataListForCellStrings(cellValues) );
appendRequest.setFields("userEnteredValue");
Request req = new Request();
req.setAppendCells( appendRequest );
return executeBatchRequest(req);
}
Then call batchUpdate on the spreadsheets() interface:
BatchUpdateSpreadsheetResponse executeBatchRequest(Request request) throws SpreadsheetException {
List<Request> requests = new ArrayList<>();
requests.add( request );
BatchUpdateSpreadsheetRequest batchRequest = new BatchUpdateSpreadsheetRequest();
batchRequest.setRequests( requests );
try {
return mService.spreadsheets().batchUpdate(mSpreadsheet.getSpreadsheetId(), batchRequest).execute();
} catch (IOException e) {
throw new SpreadsheetException(e);
}
}
Unfortunately there doesn't seem to be a way to know which rows were updated. Not does is seem possible to set valueInputOption when appending in this way.
There is actually a way to ask the API.
I am using this on my node.js client (pretty sure it's very similar)
var sheets = google.sheets('v4');
sheets.spreadsheets.get({
auth: auth,
spreadsheetId: 'spreadsheet_id',
includeGridData: true
}, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
} else {
var last_row = response.sheets[0].data[0].rowData.length;
}
});

How to add array as post parameter retrofit 2

I want to pass an array as parameter like this:
images[] = "Base64String A..."
images[] = "Base64String B..."
images[] = "Base64String C..."
images[] = "Base64String ..."
I'm using Laravel 5.1 on server side.
My controller to handle post data:
public function uploadTempImage(){
if (Input::has('images')) {
$images = Input::get('images');
$barcode_id =Input::get('barcode_id');
$upload_count=0;
foreach($images as $string_image) {
$image = base64_decode($string_image);
$filename = "$barcode_id$upload_count".time().date("d").date("m").date("Y").".jpeg";
$upload_count +=Storage::disk('local')->put("/tempImage/".$filename , $image);
}
return $upload_count;
}else{
return 'no file uploaded';
}
}
i use postman to test my server and its working..
please help me get over this.. thanks
TL;DR using arrays in form data like this is a bit problematic and hardly standardized. Avoid if possible.
Long version
Read this issue on retrofit's github
If the strings won't be too massive, consider using JSON as your transport vehicle, and declare a JSON array to store your base64 strings.

Categories

Resources