sending file to server by okhttp - android

I have this node.js code in my server side app:
app.post('/upload',function (req,resp) {
console.log(req);
var email=req.headers['email']
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null,'uploads/')
},
filename: function (req, file, cb) {
cb(null,"asdsad"+".jpg")
}
})
var upload = multer({ storage: storage }).single('propic')
upload(req, resp, function (err) {
if (err) {
throw err
}
resp.setHeader('Content-Type', 'application/json');
resp.send({status:true})
})
})
I want to send a bitmap in client side (Android) to server.
I used the okhttp library and I want to create form-data.
How can I do that?

You must send File to your server instead of bitmap and must use POST method to handle the request to Server side.
and write this codes to send your file into the server:
public class UploadService {
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
public void uploadImage(File image, String imageName) throws IOException {
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("file", imageName, RequestBody.create(MEDIA_TYPE_PNG, image))
.build();
Request request = new Request.Builder().url("http://localhost:8080/v1/upload")
.post(requestBody).build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
}
}

Related

How to show the current progress on a post request in Flutter? [duplicate]

I am uploading a file using MultipartRequest from package:http. I am successfully uploading the file but I want to get the progress of the file that is being uploaded. How can I achieve that? My current code looks something like this
Future submitFile(var report, File file) async {
var uri = Uri.parse(endpoint + "v1/reports");
var request = http.MultipartRequest("POST", uri);
await addHeaders(request.headers);
request.fields.addAll(Report.toMap(report));
if (file != null)
request.files.add(await http.MultipartFile.fromPath(
'report_resource',
file.path,
));
String response = "";
await (await request.send()).stream.forEach((message) {
response = response + String.fromCharCodes(message);
});
return response;
}
I searched for the solution, found this. And this post is somehow not similar to what I want to achieve, as he is using different client for the request.
Maybe I am not searching on the right path.
Help is appreciated.
Here is my take:
// multipart_request.dart
import 'dart:async';
import 'package:http/http.dart' as http;
class MultipartRequest extends http.MultipartRequest {
/// Creates a new [MultipartRequest].
MultipartRequest(
String method,
Uri url, {
this.onProgress,
}) : super(method, url);
final void Function(int bytes, int totalBytes) onProgress;
/// Freezes all mutable fields and returns a single-subscription [ByteStream]
/// that will emit the request body.
http.ByteStream finalize() {
final byteStream = super.finalize();
if (onProgress == null) return byteStream;
final total = this.contentLength;
int bytes = 0;
final t = StreamTransformer.fromHandlers(
handleData: (List<int> data, EventSink<List<int>> sink) {
bytes += data.length;
onProgress(bytes, total);
if(total >= bytes) {
sink.add(data);
}
},
);
final stream = byteStream.transform(t);
return http.ByteStream(stream);
}
}
Usage:
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart' show MediaType;
import 'multipart_request.dart';
final uri = 'https://...';
final request = MultipartRequest(
'POST',
uri,
onProgress: (int bytes, int total) {
final progress = bytes / total;
print('progress: $progress ($bytes/$total)');
},
);
request.headers['HeaderKey'] = 'header_value';
request.fields['form_key'] = 'form_value';
request.files.add(
await http.MultipartFile.fromPath(
'field_name',
'path/to/file',
contentType: MediaType('image', 'jpeg'),
),
);
final streamedResponse = await request.send();
After waiting for a week or so. I didn't get response. Thus I developed a plugin myself to get this behavior. Package link.
Example to use it:
var request = MultipartRequest();
request.addFile("image", imagePath);
Response response = request.send();
response.onError = () {
print("Error");
};
response.onComplete = (response) {
print(response);
};
//Not 100% success
response.progress.listen((int progress) {
print("progress from response object " + progress.toString());
});
Update Jun 30, 2020
The package now supports iOS as well.
Can you try this class as I did not test it yet, Let me know of any thing printed in your console.
class MF extends http.MultipartRequest{
MF(String method, Uri url) : super(method, url);
#override
Future<http.StreamedResponse> send() async {
var client = new Client();
int byteCount = 0;
Stream<List<int>> onDone<T>(Stream<List<int>> stream, void onDone()) =>
stream.transform(new StreamTransformer.fromHandlers(
handleDone: (sink) {
sink.close();
onDone();
},
handleData: (data, sink) {
byteCount += data.length;
print(byteCount);
sink.add(data);
},
),
);
try {
var response = await client.send(this);
var stream = onDone(response.stream, client.close);
return new StreamedResponse(new ByteStream(stream), response.statusCode,
contentLength: response.contentLength,
request: response.request,
headers: response.headers,
isRedirect: response.isRedirect,
persistentConnection: response.persistentConnection,
reasonPhrase: response.reasonPhrase);
} catch (_) {
client.close();
rethrow;
}
}
}
USAGE:
instead of var request = http.MultipartRequest("POST", uri);
use:
var request = MF("POST", uri);

How can upload image to server with Retrofit on Android

In my application, I want to upload image to the server and for this, I used Retrofit2.
I write the below codes, but after upload the image shows me a server error!
This error is: Media field is empty, please fill this!
This error is from my server and says to me the media field is empty!
My API codes:
#Multipart
#POST("/media")
fun uploadImage(
#Header(AUTHORIZATION) auth: String, #Header(ACCEPT) accept: String, #Header(CONTENT_TYPE) contentType: String,
#PartMap map: LinkedHashMap<String, RequestBody>
): Single<Response<ResponseModelUploadImage>>
My Activity codes:
var requestBody: RequestBody
var body: MultipartBody.Part
val mapRequestBody = LinkedHashMap<String, RequestBody>()
Log.e("filePath",uploadNaturalImageFile.toString())
requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), uploadNaturalImageFile);
mapRequestBody.put("media\"; media=\"" + uploadNaturalImageFile.name, requestBody);
presenter.callUploadImage(userToken, APPLICATION_JSON, APPLICATION_JSON, mapRequestBody)
But when upload this image with Postman, everything is OK and doesn't have any problem!
Postman request image:
UPDATE : I see my log and show me name=media, but server again media is empty!
My logcat messages:
D/OkHttp: Content-Disposition: form-data; name="media"; filename="JPEG_20201108_1623315560915977415445829.jpg"
Why show me this error? how can I fix it?
in your RetrofitService.java
#Multipart
#POST("/app/uploadFile.do")
Call<JsonObject> uploadFile(#PartMap() LinkedHashMap<String, RequestBody> partMap, #Part List<MultipartBody.Part> names);
and in your activity,
public static void fileUpload (File file) {
Log.d(TAG, "file===" + file.getName());
RequestBody requestBody;
MultipartBody.Part body;
LinkedHashMap<String, RequestBody> mapRequestBody = new LinkedHashMap<String, RequestBody>();
List<MultipartBody.Part> arrBody = new ArrayList<>();
requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
mapRequestBody.put("file\"; filename=\"" + file.getName(), requestBody);
mapRequestBody.put("test", RequestBody.create(MediaType.parse("text/plain"), "gogogogogogogog"));
body = MultipartBody.Part.createFormData("fileName", file.getName(), requestBody);
arrBody.add(body);
Call<JsonObject> call = RetrofitImg.getInstance().getService().uploadFile(mapRequestBody, arrBody);
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
if (response.body() != null) {
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
Log.e(TAG + "Err", t.getMessage());
}
});
}
UPDATA : I found other example
#POST("my/files/photo/")
Call<FileUploadResponse> uploadPhoto(#Header("Content-Type") String contentType,
#Header("Authorization") String auth,
#Body MultipartBody body);
and
ApiClient.ApiInterface client = ApiClient.getClient();
File file = new File(getPathFromUri(fileUri));
RequestBody fileBody = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file);
MultipartBody body = new MultipartBody.Builder().addFormDataPart("file-type", "profile")
.addFormDataPart("photo", "image.png", fileBody)
.build();
client.uploadPhoto("multipart/form-data; boundary=" + body.boundary(),
PrefManager.getInstance().getToken(), body);

Convert OkHttp's Form Data (Android) to Axios(React Native)'s Form Data

I'm recreating a project that was originally designed for Native Android to use React Native. There is an endpoint that is responsible to send a image using Form Data. I tried to convert the OkHttp3's Form Data to Axios's Form Data and I'm getting an error from backend saying that the request fields doesn't match.
My Code so far:
- Native Android(original app):
public RequestResponse<Boolean> functionApi(#NonNull String id, String imageExtension, #NonNull byte[] imageData, #NonNull String anotherData) throws ServerErrorException, IOException, AuthenticationException {
String path = "route/" + id;
Pair<String, String> contentTypeHeader = new Pair<>("Content-Type", "multipart/form-data");
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("anotherData", anotherData)
.addFormDataPart("imageData", id + "." + imageExtension, RequestBody.create(MediaType.parse("image/png"), imageData))
.build();
Response response = MyHttpClient.execute(path, "POST", requestBody, contentTypeHeader);
String body = response.body().string();
RequestResponse<Boolean> r = responseBodyToObject(body, RequestResponse.class);
r.setBody(r.getStatus() != RequestResponse.ERROR);
return r;
}
React Native(new app) version:
export const functionApi = async(id,imageExtension,imageData,anotherData)=>{
try{
let formData = new FormData()
formData.append('anotherData',anotherData)
formData.append('imageData',`data:image/${imageExtension};base64,${imageData}`,`${id}.${imageExtension}`)
//imageData here i tried to use a base64's string
let res = await axios({
url:`${URL_SERVER}/route/${id}`,
method:'POST',
headers:{
'Content-Type':"multipart/form-data"
},
data:formData
})
return res['data']
}catch(err){
return getErrorMessage(err)
}
}
I got a solution that finally worked for me:
export const functionApi = async(id,imageExtension,imageData,anotherData)=>{
try{
let formData = new FormData()
formData.append('anotherData',anotherData)
formData.append('imageData',{
uri: imageData['uri'],
type: 'image/jpg',
name: `${id}.${imageExtension}`,
})
let res = await axios({
url:`${URL_SERVER}/route/${id}`,
method:'POST',
headers:{
'Content-Type':'multipart/form-data'
},
data:formData
})
return res['data']
}catch(err){
return getErrorMessage(err)
}
}

RNFetchBlob can not download apk, But in the Browser the url can dowload file

I develop a react-native app, for android, I use the RNFetchBlob to download the APK. and in android, RNFetchBlob uses OkHttp library to download the file.
But the APK can not download. when I put the link in android, it can
download the file successfully.
I try to replace it us the other URL, RNFetchBlob can download the file.
But my URL is a simple url: http://host:port/AppDownLoad/publish/some.apk
the RNFetchBlob config is the below:
RNFetchBlob.config({
path: dirs.DCIMDir + "/Some.apk",
timeout:180000,
})
.fetch("GET", APK_URL)
.progress({ count: 10 }, (received, total) => {
let currentProgress = received / total;
})
.then(res => {
if(res.respInfo.timeout){
Linking.openURL(APKURL)
return;
}
android.actionViewIntent(
res.path(),
"application/vnd.android.package-archive"
);
})
.catch(error => {
console.log(error);
// Linking.openURL(APKURL)
});
the third library OkHttp source code is:
OkHttpClient.Builder clientBuilder;
if (this.options.trusty) {
clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient(client);
} else {
clientBuilder = client.newBuilder();
}
final Request.Builder builder = new Request.Builder();
try {
builder.url(new URL(url));
} catch (MalformedURLException e) {
e.printStackTrace();
}
builder.method("GET", null);
final Request req = builder.build();
clientBuilder.connectionPool(pool);
clientBuilder.retryOnConnectionFailure(false);
clientBuilder.followRedirects(options.followRedirect);
clientBuilder.followSslRedirects(options.followRedirect);
clientBuilder.retryOnConnectionFailure(true);
OkHttpClient client = enableTls12OnPreLollipop(clientBuilder).build();
Call call = client.newCall(req);
taskTable.put(taskId, call);
call.enqueue(new okhttp3.Callback(),{callback}
the error info is the content-length is disagreed with the stream Could anyone tell me why?

AWS Lambda : errorMessage Process exited before completing request

Hi I'm newbie in android!
I want to upload image file from android client to server(Server makes thumbnail, and return thumbnail's url).
However I stucked in this error message.
{"errorMessage":"RequestId: 8e2a21b8-e62e-11e8-8585-d9b6fdfec9b9 Process exited before completing request"}!
I tried to find this error code in stackoverflow, but i cannot found answer for android.
Please help or give me link where I can solve this problem...
Here is server code.
const AWS = require('aws-sdk');
const multipart = require("parse-multipart");
const s3 = new AWS.S3();
const bluebird = require('bluebird');
exports.handler = function(event, context) {
let result = [];
const bodyBuffer = new Buffer(event['body-json'].toString(), 'base64');
const boundary = multipart.getBoundary(event.params.header['Content-Type']);
const parts = multipart.Parse(bodyBuffer, boundary);
const files = getFiles(parts);
return bluebird.map(files, file => {
console.log('UploadCall');
return upload(file)
.then(
data => {
result.push({
'bucket': data.Bucket,
'key': data.key,
'fileUrl': file.uploadFile.fullPath })
console.log( `DATA => ${JSON.stringify(data, null, 2 )}`);
},
err => {
console.log(`S3 UPLOAD ERR => ${err}`);
}
)
})
.then(_=> {
return context.succeed(result);
});
}
let upload = function(file) {
console.log('PutObject Call')
return s3.upload(file.params).promise();
};
let getFiles = function(parts) {
let files = [];
parts.forEach(part => {
const buffer = part.data
const fileName = part.filename;
const fileFullName = fileName;
const originBucket = 'dna-edge/images';
const filefullPath = `https://s3.ap-northeast-2.amazonaws.com/${originBucket}/${fileFullName}`;
const params = {
Bucket: originBucket,
Key: fileFullName,
Body: buffer
};
const uploadFile = {
size: buffer.toString('ascii').length,
type: part.type,
name: fileName,
fullPath: filefullPath
};
files.push({ params, uploadFile })
});
return files;
};
And this is client code.(imgURL looks like /storage/emulated/0/DCIM/img/1493742568136.jpg)
public static String requestHttpPostLambda(String url, String imgURL){
/*
await axios.post(`${AWS_LAMBDA_API_URL}?type=${type}`, formData,
{ headers: { 'Content-Type': 'multipart/form-data' }})
.then((response) => {result = response});
*/
String result=null;
try {
HttpClient client = new DefaultHttpClient();
String postURL = url;
HttpPost post = new HttpPost(postURL);
post.setHeader("Content-Type", "multipart/form-data");
File file = new File(imgURL);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addPart("image", new FileBody(file));
post.setEntity(builder.build());
HttpResponse responsePOST = client.execute(post);
Log.e("HttpResponse", responsePOST.getStatusLine()+"");
HttpEntity resEntity = responsePOST.getEntity();
if (resEntity != null) {
result = EntityUtils.toString(resEntity);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
Welcome to stackoverflow.
So for some reason AWS aren't too good an updating the docs, don't use context.succeed, use the callback thats passed as a third param.
Also I'd move to Node 8.10 runtime because then rather than using promises/then pattern you can use async/await.
export default(event, context, callback) => {
try {
// do some stuff
callback(null, SOME_VALID_HTTP_RESPONSE)
} catch(e){
callback(e, null)
}
}
There's a few reason your Lambda could be failing, if the process exited before completing it's either crashing OR you're not returning a valid HTTP response(if your lambda is behind API gateway)
Two solutions - first place to look is in cloudwatch, find your lambda function name and check the latest log to look for error messages.
Second - check out my answer here so when your function succeeds you need to return a valid HTTP response to API Gateway so in essence if you use my code from there you can do:
callback(null, responder.success({someJson: someValue}))
Any questions let me know :-)
EDIT: I'm updating this question I'm just working on an example for a multiple file upload to S3!

Categories

Resources