I am getting the following exception when trying to download a ParseFile using getData() method.
com.parse.ParseException: Download from S3 failed. Moved Permanently
at com.parse.ParseAWSRequest.onResponseAsync(ParseAWSRequest.java:43)
at com.parse.ParseRequest$3.then(ParseRequest.java:137)
at com.parse.ParseRequest$3.then(ParseRequest.java:133)
at bolts.Task$15.run(Task.java:917)
at bolts.BoltsExecutors$ImmediateExecutor.execute(BoltsExecutors.java:105)
at bolts.Task.completeAfterTask(Task.java:908)
at bolts.Task.continueWithTask(Task.java:715)
at bolts.Task.continueWithTask(Task.java:726)
at bolts.Task$13.then(Task.java:818)
at bolts.Task$13.then(Task.java:806)
at bolts.Task$15.run(Task.java:917)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
The ParseFile is a jpg which is uploaded and then put into a ParseObject called ImageObject. The ImageObject has several other parameters like caption, width, height which upload and download correctly. The image itself uploads correctly as confirmed through the dashboard. Only during the download I get the above exception.
This is my upload code
file.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.d(tag, "Successfully uploaded image file");
ParseObject image = new ParseObject("imageObject"); //Create new Image-ParseObject and set values
image.put("author", ParseUser.getCurrentUser());
image.put("imageFile", file);
image.put("caption", caption);
image.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if(e==null){
Log.d(tag, "Successfully saved image file to object");
}else{
Log.d(tag, "Failed to save image file to object", e);
}
}
});
} else {
Log.d(tag, "Failed to save image", e);
}
}
my download code is pretty much ParseFile.getFile() after I have retrieved all the ImageObjects through a ParseQuery.
I Followed Line 43 of ParseAWSRequest.java and I found this,
if (statusCode >= 200 && statusCode < 300 || statusCode == 304) {
// OK
} else {
String action = method == ParseHttpRequest.Method.GET ? "Download from" : "Upload to";
return Task.forError(new ParseException(ParseException.CONNECTION_FAILED, String.format(
"%s S3 failed. %s", action, response.getReasonPhrase())));
}
This and the "Permanently removed" points to my nginx reverse proxy, which returns a 301 code in the redirect to https. Unfortunately I am not sure where to proceed from here.
Extra information,
I have blocked port 80(http) in the firewall for a test and it seems that for the download parse-android-sdk is trying to download from http. Which is odd because I have specified my parse-server link with "https", and the fact that upload is working fine. I can see the uploaded image using dashboard on my server.
Using,
Parse Server 2.2.13
Parse Android SDK 1.13.1
Parse Dashboard 1.0.14
I found a workaround to this issue just by directly downloading the file. I used ParseFile.getUrl() method to get the url and then use that to download and use the file.
In my case it was .jpg files. And then I used an external library that takes care of all the downloading/caching/loading into imageviews.
Both of these libraries work well
https://github.com/koush/ion
http://square.github.io/picasso/
I am currently trying to upload a file from an android client using retrofit2 to a server using Spring Boot and its REST api.
CLIENT
I specifiy the upload method as described here: https://github.com/square/retrofit/issues/1063
public interface RetroRespondService {
#Multipart
#POST("/v1/answers")
public Call<ResponseDTO> sendPictures(#Part("file\"; filename=\"image.png")RequestBody image);
}
In another class the method to provide the actual image is declared:
(Now its just a test scenario. When image uploading is actually accomplished it will get more sophisticated.)
public void performAnswerRequest() {
try {
if (mRetrofit == null) {
mRetrofit = new Retrofit.Builder()
.baseUrl(DataHolder.getHostName())
.build();
}
//load test image
AssetManager manager = getAssets();
File file = new File(getFilesDir(), "image.png");
Utility.writeBytesToFile(new BufferedInputStream(manager.open("heart.png")), file);
RetroRespondService requestService = mRetrofit.create(RetroRespondService.class);
RequestBody image= RequestBody.create(MediaType.parse("multipart/form-data"), file);
Call<ResponseDTO> response = requestService.sendPictures(image);
response.enqueue(new AsyncAnswerResponse());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
SERVER
What I actually do not know is, how to properly get the image on the spring side.
#RequestMapping(value = API_VERSION + "/answers", method = RequestMethod.POST)
#ResponseBody
ResponseEntity<ResponseDTO> addAnswers(#RequestParam("file\"; filename=\"image.png") MultipartFile answers) throws DBEntryDoesNotExistException, EvaluationException, ParticipantException {
// In fact I have set a brake point here. Never entered the method yet, though
System.out.println("Yay!")
return null;
}
ERROR
Request: localhost:8080/v1/answers raised org.springframework.web.bind.MissingServletRequestParameterException:
Required MultipartFile parameter 'file"; filename="image.png' is not present
Since wireshark reports that in fact a request of size 1894 Bytes was send and this is the size of the image i want to upload I strongly believe the the data is actually transmitted but cannot be decoded from the server.
I have also seen this answers: How to config "CommonsMultipartResolver" in spring4 without xml to upload file
and subsequently implemented this class on the server side:
#Configuration
public class MultipartConfiguration {
#Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver=new CommonsMultipartResolver();
resolver.setDefaultEncoding("utf-8");
resolver.setMaxUploadSize(1048576);
return resolver;
}
}
If you have any pointers in how to solve this I would appreciate your answer tremendously :)
If there are any questions left unanswered feel free to ask away.
Btw.: Sending and receiving JSON encoded data works just fine in both directions.
My application is very image centric and would like to allow the user to share images generated by the application on facebook posts. I am of the understanding that "User Generated" can only be used with user/camera taken photos so that is not an option. Correct?
Since I can't specify user generated, I'd like to either:
Set the link on the feed image such that the image in the album is opened, not the default og:url.
This link is by default the og:url specified in the open graph object.
I still need all of the other og:url links in the post except the image to point to the website page.
-OR-
Put an href link in the description (or somewhere else in the feed post) that opens the image in the album.
Unless I am mistaken, It doesn't seem that HTML href tags are allowed in posts/stories. Is there a way to do this in the caption of a custom story?
I am able to upload the image to the app's album and obtain the URL to the image in the album (also using "no story" so that it doesn't create a post).
Is what I'm trying to do possible?
How do I garner more control over the links set in a facebook feed post?
EDIT:
Regarding "User Generated" from facebook docs:
"The User Generated Photos action capability can only be used if the photos are original and taken by a user with an actual camera."
I don't want to generate two stories. Ideally I want ONE story that features an image that can be clicked on that links to the original full size image in the app album. The rest of the story shares some details about the image (desc. etc.). So I guess, in a way, I'm looking for a workaround for the camera/photo requirement.
I don't mind the standard story layout (with a smaller image with title, description/captions) but just want to link that image to one in the users application album.
EDIT:
I'm realizing that an Open Graph Story isn't the way to go here. I have instead gone to simply using the Graph API. (https://developers.facebook.com/docs/graph-api/reference/user/photos/) This allows me to post a link in the user comments along with a photo. For anyone interested:
public static void doImagePost(final Bitmap _sideBySide, String _comments) {
Bundle parameters = new Bundle();
parameters.putParcelable("image", _sideBySide);
String message;
if(_comments != null)
{
if(!_comments.isEmpty())
{
message = _comments + '\n' + '\n' + JSBridge.getURL();
} else {
message = JSBridge.getURL();
}
} else {
message = JSBridge.getURL();
}
//Plain Http://blahblahblah.com/foobar/whatever gets turned into a link in the message field
parameters.putString("message", message);
Bitmap redSideBySide = EHelper.reduceImageSize(_sideBySide, (int)4E6);
Session session = Session.getActiveSession();
if(session == null) {
requestPublish(MODE_PHOTO, redSideBySide, _comments);
return;
} else {
if(!session.isOpened())
{
requestPublish(MODE_PHOTO, redSideBySide, _comments);
return;
}
}
//Here is the Graph API request.
Request imagePostRequest = new Request(Session.getActiveSession(), "me/photos" , parameters, HttpMethod.POST, new Request.Callback() {
#Override
public void onCompleted(Response response) {
if(response.getError() == null)
{
Toast.makeText(m_Context, "Post image success", Toast.LENGTH_SHORT).show();
//m_Images = null;
dismissProgressDlg();
} else {
Toast.makeText(m_Context, "Error posting image to facebook: " + response.getError().getErrorMessage(), Toast.LENGTH_SHORT).show();
Log.e(DEBUG_TAG, "Error posting image to facebook: " + response.getError().getErrorMessage());
dismissProgressDlg();
}
}
});
showProgressDialog("Posting to Facebook...");
imagePostRequest.executeAsync();
}
I recently started to use Volley lib from Google for my network requests. One of my requests get error 301 for redirect, so my question is that can volley handle redirect somehow automatically or do I have to handle it manually in parseNetworkError or use some kind of RetryPolicyhere?
Thanks.
Replace your url like that url.replace("http", "https");
for example:
if your url looking like that : "http://graph.facebook......." than
it should be like : "https://graph.facebook......."
it works for me
I fixed it catching the http status 301 or 302, reading redirect url and setting it to request then throwing expection which triggers retry.
Edit: Here are the main keys in volley lib which i modified:
Added method public void setUrl(final String url) for class Request
In class BasicNetwork is added check for redirection after // Handle cache validation, if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY) || statusCode == HttpStatus.SC_MOVED_TEMPORARILY), there I read the redirect url with responseHeaders.get("location"), call setUrl with request object and throw error
Error get's catched and it calls attemptRetryOnException
You also need to have RetryPolicy set for the Request (see DefaultRetryPolicy for this)
If you dont want to modify the Volley lib you can catch the 301 and manually re-send the request.
In your GsonRequest class implement deliverError and create a new Request object with the new Location url from the header and insert that to the request queue.
Something like this:
#Override
public void deliverError(final VolleyError error) {
Log.d(TAG, "deliverError");
final int status = error.networkResponse.statusCode;
// Handle 30x
if(HttpURLConnection.HTTP_MOVED_PERM == status || status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_SEE_OTHER) {
final String location = error.networkResponse.headers.get("Location");
Log.d(TAG, "Location: " + location);
final GsonRequest<T> request = new GsonRequest<T>(method, location, jsonRequest, this.requestContentType, this.clazz, this.ttl, this.listener, this.errorListener);
// Construct a request clone and change the url to redirect location.
RequestManager.getRequestQueue().add(request);
}
}
This way you can keep updating Volley and not have to worry about things breaking.
Like many others, I was simply confused about why Volley wasn't following redirects automatically. By looking at the source code I found that while Volley will set the redirect URL correctly on its own, it won't actually follow it unless the request's retry policy specifies to "retry" at least once. Inexplicably, the default retry policy sets maxNumRetries to 0. So the fix is to set a retry policy with 1 retry (10s timeout and 1x back-off copied from default):
request.setRetryPolicy(new DefaultRetryPolicy(10000, 1, 1.0f))
For reference, here is the source code:
/**
* Constructs a new retry policy.
* #param initialTimeoutMs The initial timeout for the policy.
* #param maxNumRetries The maximum number of retries.
* #param backoffMultiplier Backoff multiplier for the policy.
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
Alternatively, you can create a custom implementation of RetryPolicy that only "retries" in the event of a 301 or 302.
Hope this helps someone!
End up doing a merge of what most #niko and #slott answered:
// Request impl class
// ...
#Override
public void deliverError(VolleyError error) {
super.deliverError(error);
Log.e(TAG, error.getMessage(), error);
final int status = error.networkResponse.statusCode;
// Handle 30x
if (status == HttpURLConnection.HTTP_MOVED_PERM ||
status == HttpURLConnection.HTTP_MOVED_TEMP ||
status == HttpURLConnection.HTTP_SEE_OTHER) {
final String location = error.networkResponse.headers.get("Location");
if (BuildConfig.DEBUG) {
Log.d(TAG, "Location: " + location);
}
// TODO: create new request with new location
// TODO: enqueue new request
}
}
#Override
public String getUrl() {
String url = super.getUrl();
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "http://" + url; // use http by default
}
return url;
}
It worked well overriding StringRequest methods.
Hope it can help someone.
Volley supports redirection without any patches, no need for a separate fork
Explanation:
Volley internally uses HttpClient which by default follows 301/302 unless specified otherwise
From: http://hc.apache.org/httpcomponents-client-4.2.x/tutorial/html/httpagent.html
ClientPNames.HANDLE_REDIRECTS='http.protocol.handle-redirects': defines whether redirects should be handled automatically. This parameter expects a value of type java.lang.Boolean. If this parameter is not set HttpClient will handle redirects automatically.
ok, im a bit late to the game here, but i've recently been trying to achieve this same aspect, so https://stackoverflow.com/a/17483037/2423312 is the best one, given that you are willing to fork volley and maintain it and the answer here : https://stackoverflow.com/a/27566737/2423312 - I'm not sure how this even worked.This one is spot on though : https://stackoverflow.com/a/28454312/2423312. But its actually adding a new request object to the NetworkDipatcher's queue, so you'll have to notify the caller as well somehow, there is one dirty way where you can do this by not modifying the request object + changing the field "mURL", PLEASE NOTE THAT THIS IS DEPENDENT ON YOUR IMPLEMENTATION OF VOLLEY'S RetryPolicy.java INTERFACE AND HOW YOUR CLASSES EXTENDING Request.java CLASS ARE, here you go : welcome REFLECTION
Class volleyRequestClass = request.getClass().getSuperclass();
Field urlField = volleyRequestClass.getDeclaredField("mUrl");
urlField.setAccessible(true);
urlField.set(request, newRedirectURL);
Personally I'd prefer cloning volley though. Plus looks like volley's example BasicNetwork class was designed to fail at redirects : https://github.com/google/volley/blob/ddfb86659df59e7293df9277da216d73c34aa800/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java#L156 so i guess they arent leaning too much on redirects, feel free to suggest/edit. Always looking for good way..
I am using volley:1.1.1 with https url though the request was having some issue. On digging deeper i found that my request method was getting changed from POST to GET due to redirect (permanent redirect 301). I am using using nginx and in server block i was having a rewrite rule that was causing the issue.
So in short everything seems good with latest version of volley. My utility function here-
public void makePostRequest(String url, JSONObject body, final AjaxCallback ajaxCallback) {
try {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,
url, body, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d(LOG, response.toString());
ajaxCallback.onSuccess(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(LOG, error.toString());
ajaxCallback.onError(error);
}
});
singleton.getRequestQueue().add(jsonObjectRequest);
} catch(Exception e) {
Log.d(LOG, "Exception makePostRequest");
e.printStackTrace();
}
}
// separate file
public interface AjaxCallback {
void onSuccess(JSONObject response);
void onError(VolleyError error);
}
Well Ive looked for hours and hours after an answer but havent really found anything that I could use.
I want to be able to send/upload files (images, videos, documents, audio) to a webservice. I want to do this from an android device (version 2.2).
I need to be able to send this to a tailormade wcf for my needs, I need to send information with the file in order to verify the user who wants to upload.
Also worth mentioning is I use a restful wcf .net webservice which I prefer to continue to use if possible. Atleast I need to be able to use wcf .net as webservice for the communication.
All solutions Ive seen concentrate on the android part and never on the service part. I need both. :)
This blog post
http://reecon.wordpress.com/2010/04/25/uploading-files-to-http-server-using-post-android-sdk/
describes how you can upload files with HTTP POST - which you must use from Android if you want to upload files to a WCF service wired up with webHttpBinding.
Extra metadata can be sent as HTTP headers.
--larsw
Ion.with(getActivity())
.load(URL)
.setMultipartFile("upload", "image/jpeg", fileToUpload)
.asString()
.setCallback(new FutureCallback<String>() {
#Override
public void onCompleted(Exception arg0, String result) {
hideProgressDialog();
if(result!=null){
CoreFragment.this.resultCallBack.returnResult(result.toString());
} else {
showErrorToast("Error");
}
}
});
and wcf is
public string upload(Stream stream)
{
MultipartParser parser = new MultipartParser(stream);
if (parser.Success)
{
try
{
string strServerpath = #"C:\IISWebsite\DOTNET\BGGTS\Files\AttendanceProof\" + parser.Filename;
File.WriteAllBytes(strServerpath, parser.FileContents);
}
catch (Exception ex)
{
return "Failed";
}
}
return "Success";
}