I'm currently developing an android application that takes in a file and converts it into a string. The string is then sent over to a server and I'm using a MVC application in this case. The string has to be sent to a server first because I needed to do some processing before storing it. Below is my code for the MVC application.
[HttpPost]
public HttpResponseMessage RetrieveFile(String fileString)
{
var response = new HttpResponseMessage()
{
Content = new StringContent("{\"File\":\"" + fileString + "\"}")
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
}
However, whenever I call the http://localhost/api/TestCon/RetrieveFile?fileString=*Very Long String* . it throws a "Request URL Too Long" error message. What do I have to do to send a large about of data, possibly a few MBs, over to the server?
Related
I'm building this android app where I need to send images to the server. I'm using retrofit to do so. I get image path as its answer in this stack overflow question Get file path of image on Android and send images as shown here How to upload an image file in Retrofit 2. When I decode a file that I create in the android studio it is valid, but when I send it to my .net rest API it is null. Here is the rest API code, the idea is to get an image if it's null send an image that says it's null, and if not save that image and send it back. The part where I'm sending it back works perfectly, that is I receive an image sent to android, but the image that is sent from android is null.
[Route("post")]
public async Task<IActionResult> PostSlika(IFormFile formFile)
{
if (formFile is null)
{
var stream2 = new FileStream(Path.Combine(_host.WebRootPath, "Images/null_je.jpg"), FileMode.Open);
return File(stream2, "image/jpg");
}
using (var stream = System.IO.File.Create(Path.Combine(_host.WebRootPath, "Images/1.jpg")))
{
await formFile.CopyToAsync(stream);
}
var stream1 = new FileStream(Path.Combine(_host.WebRootPath, "Images/1.jpg"), FileMode.Open);
return File(stream1, "image/jpg");
}
}
I guess there is a conversion problem when converting data to IFormFile. But first check the incomming data with Base64String. I mean:
[Route("post")]
public async Task<IActionResult> PostSlika(string formFileStr)
{
//Convert formFileStr from Base64String to IFormFile
}
Note: you should send Base64String to your service to test it.
If that is OK but you don't want to use this approach then you can use UriTemplate like this:
[WebInvoke( Method= "POST",
//These two parameters are not necessary, but you can use them:
RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
//
UriTemplate = "PostSlika/?q={formFile}")]
public async Task<IActionResult> PostSlika(IFormFile formFile)
{
//your codes here
}
Then address to your service method should be changed a little:
{Your Service Address}/PostSlika/?q={object of IFormFile}
Here's my problem: I'm writing a laravel backend which have to serve an mp3 file that had to be reproduced by using the android standard media player.
For the laravel backend I need to use JWT to handle authentication so on every request headers I have to set the "Authorization" field to "Bearer {token}" .The laravel route is "/songs/{id}" and is handled in this way:
public function getSong(Song $song) {
$file = new File(storage_path()."/songs/".$song->path.".mp3");
$headers = array();
$headers['Content-Type'] = 'audio/mpeg, audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3';
$headers['Content-Length'] = $file->getSize();
$headers['Content-Transfer-Encoding'] = 'binary';
$headers['Accept-Range'] = 'bytes';
$headers['Cache-Control'] = 'must-revalidate, post-check=0, pre-check=0';
$headers['Connection'] = 'Keep-Alive';
$headers['Content-Disposition'] = 'attachment; filename="'.$song->path.'.mp3"';
$user = \Auth::user();
if($user->activated_at) {
return Response::download($file, $song->path, $headers);
}
\App::abort(400);
}
On the android side I'm using the MediaPlayer to stream the mp3 file in this way:
media_player = new MediaPlayer();
try {
media_player.setAudioStreamType(AudioManager.STREAM_MUSIC);
String token = getSharedPreferences("p_shared", MODE_PRIVATE).getString("token", null);
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + token);
media_player.setDataSource(
getApplicationContext(),
Uri.parse(ConnectionHelper.SERVER + "/songs/" + song.getId()),
headers
);
} catch (IOException e) {
finish();
Toast.makeText(
Round.this,
"Some error occurred. Retry in some minutes.",
Toast.LENGTH_SHORT
).show();
}
media_player.setOnCompletionListener(this);
media_player.setOnErrorListener(this);
media_player.setOnPreparedListener(this);
But every time I execute the code I get extra code -1005 on the error listener that means ERROR_CONNECTION_LOST.
The problem: Response::download(...) doesn't produce a stream, so I can't serve my .mp3 file.
The solution:
As Symfony HttpFoundation doc. says in the serving file paragraph:
"if you are serving a static file, you can use a BinaryFileResponse"
The .mp3 files I need to serve are statics in the server and stored in "/storage/songs/" so I decided to use the BinaryFileResponse, and the method for serving .mp3 became:
use Symfony\Component\HttpFoundation\BinaryFileResponse;
[...]
public function getSong(Song $song) {
$path = storage_path().DIRECTORY_SEPARATOR."songs".DIRECTORY_SEPARATOR.$song->path.".mp3");
$user = \Auth::user();
if($user->activated_at) {
$response = new BinaryFileResponse($path);
BinaryFileResponse::trustXSendfileTypeHeader();
return $response;
}
\App::abort(400);
}
The BinaryFileResponse automatically handle the requests and allow you to serve the file entirely (by making just one request with Http 200 code) or splitted for slower connection (more requests with Http 206 code and one final request with 200 code).
If you have the mod_xsendfile you can use (to make streaming faster) by adding:
BinaryFileResponse::trustXSendfileTypeHeader();
The android code doesn't need to change in order to stream the file.
I am using the Salesforce SDK (4.1.x) in a native Android app. I use the RestClient.sendAsync method to post my form data to a custom object. That part is working fine. Now I need to upload and attach a photo that was taken by the mobile user. I see that RestClient has an uploadFile method. Is this the correct method? If so then how do I connect the uploaded file to the custom form data?
Ok. I figured this out. First, create the parent object (the main form data) using the following.
request = RestRequest.getRequestForCreate(apiVersion, objectType, fields);
client.sendAsync(restRequest, new RestClient.AsyncRequestCallback() {...
In the onSuccess method you will get the id of the new object from the response. There are plenty of examples that show how to get the JSON object and the id. Armed with this parentId we can now create the attachment. The code looks something like this.
private void postImageAsAttachment(String parentId, String title) {
Map<String, Object> fields = new HashMap<String, Object>();
fields.put("Name", title);
fields.put("ParentId", parentId);
fields.put("Body", ImageHelper.getBase64FromImage(mCurrentPhotoPath));
RestRequest request = null;
try {
request = RestRequest.getRequestForCreate(apiVersion, "Attachment", fields);
} catch (Exception ex) {
Log.d(TAG, "sendRequest: ", ex);
Toast.makeText(MainActivity.this, "The file upload failed: " + ex.toString(), Toast.LENGTH_LONG).show();
}
client.sendAsync(request, new RestClient.AsyncRequestCallback() {...
I'm using a simple class called ImageHelper that simply loads the image file, performs image compression (if necessary), and base64 encodes the image data. The result is that an "Attachment" object is created as a child of the parent object.
I hope this helps the next person.
I have an ASP.NET C# MVC4 Web site that I have working wonderfully for the most part. However, when we tested on mobile, the cookies that I am using for authentication would not work. I set the Auth cookie in my controller action but when trying to access them on the next call they are not there. Once again this is ONLY A PROBLEM ON MOBILE. Works fine in desktop versions of IE, Chrome and Firefox. Does not work with Chrome on Android.
Code to write cookie (in controller action):
//Set information into object that can be read out of the cookie later
FormsAuthModel UserDataObj = new FormsAuthModel
{
UserID = dmUser.ID,
PasswordChange = dmUser.PasswordChange
};
string UserData = Convert.ToBase64String(clsShared.Serialize(UserDataObj));
//Create the ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, dmUser.UserName, DateTime.Now, DateTime.Now.AddDays(1), false, UserData, FormsAuthentication.FormsCookiePath);
// Encrypt the ticket
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
System.Web.HttpContext.Current.Response.Cookies.Add(cookie);
Code to read cookie (in Global.asax.cs - Application_PostAuthenticateRequest):
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
try
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
UserDataObj = (FormsAuthModel)clsShared.Deserialize(Convert.FromBase64String(authTicket.UserData), typeof(FormsAuthModel));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
//WriteEvent(string.Format("Error deserializing auth ticket - {0}", ex.Message), EventLogEntryType.Error);
}
}
The AuthCookie is always null on the subsequent requests. What the user sees is a login screen, they fill it out and they get redirected right back to the login screen.
I could not find anything in my searches that helped explain why all the mobile requests (my phone, my tablet and other users' phones) would act differently than the desktop browsers.
Any help would be greatly appreciated.
Thanks!!
OK I found a solution although I am not sure why. I changed the cookie creation code as follows and it worked.
//Set information into object that can be read out of the cookie later
FormsAuthModel UserDataObj = new FormsAuthModel
{
UserID = dmUser.ID,
PasswordChange = dmUser.PasswordChange
};
string UserData = Convert.ToBase64String(clsShared.Serialize(UserDataObj));
//Create the ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, dmUser.UserName, DateTime.Now, DateTime.Now.AddDays(1), false, UserData, FormsAuthentication.FormsCookiePath);
// Encrypt the ticket
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie - FIX IS HERE!!!
Response.Cookies[FormsAuthentication.FormsCookieName].Value = encTicket;
//HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
//Response.Cookies.Add(cookie);
Notice that the only change is in adding the cookie by setting the value directly instead of creating a cookie object and adding that to the collection.
i.e. - Response.Cookies["Name"] = Value;
I got the idea from this MS article: https://msdn.microsoft.com/en-us/library/ms178194.aspx.
So does anyone know why this would make a difference? I have used the cookie instance method several times before and never had this problem.
i am working on a Cakephp 2.x .. i am sending data from my android app to my Cakephp web app through HTTP Post and then saving into the database..
here is my code
public function message(){
$this->loadModel('Message');
if ($this->request->isPost()){
$json = $this->request->data('json');
$data = json_decode($json, TRUE);
foreach($data as $datas){
$mobileNo = $datas['mobileNo'];
$body = $datas['body'];
$type = $datas['type'];
$userId = $datas['idUser'];
$this->request->data['Message']['mobileNo'] = $mobileNo;
$this->request->data['Message']['body'] = $body;
$this->request->data['Message']['type'] = $type;
$this->request->data['Message']['User_id'] = $userId;
$this->request->data['Message']['dateTime'] = null;
$this->Message->save($this->request->data);
}
}
}
i am getting data successfully because when i print out the data
$mobileNo = $datas['mobileNo'];
it is successfully printing the number ... but dont know why it is throwing me errors on my android app and not saving the data into the database ... i think the problem is related to the Model 'Message'
You are missing to call $this->Message->create(); before the save because you're calling save() in a loop. See http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-create-array-data-array
Also check your validation rules and if your android app fails, well, do you send a proper success or error status back to the android app?
Best would be to put the data processing into a model method and unit test that method.