Reputation: 2200
I am using android to try and upload a file to my server, which is running an asp.net mvc4 web api. My android class is as follows:
package com.example.wsis;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class UploadImage extends Activity implements View.OnClickListener {
Button upload_btn, uploader;
Uri currImageURI;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload_image);
upload_btn = (Button) this.findViewById(R.id.btnUpload);
uploader = (Button) this.findViewById(R.id.btnUploadFile);
upload_btn.setOnClickListener(this);
uploader.setOnClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.upload_image, menu);
return true;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnUpload:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"),1);
break;
case R.id.btnUploadFile:
HttpUploader uploader = new HttpUploader();
try {
String image_name = uploader.execute(getRealPathFromURI(currImageURI)).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
break;
}
}
// To handle when an image is selected from the browser
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == 1) {
// currImageURI is the global variable I’m using to hold the content:
currImageURI = data.getData();
System.out.println("Current image Path is ----->" + getRealPathFromURI(currImageURI));
TextView tv_path = (TextView) findViewById(R.id.path);
tv_path.setText(getRealPathFromURI(currImageURI));
}
}
}
//Convert the image URI to the direct file system path of the image file
public String getRealPathFromURI(Uri contentUri) {
String [] proj={MediaStore.Images.Media.DATA};
android.database.Cursor cursor = managedQuery( contentUri,
proj, // Which columns to return
null, // WHERE clause; which rows to return (all rows)
null, // WHERE clause selection arguments (none)
null); // Order-by clause (ascending by name)
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
public class HttpUploader extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... path) {
String outPut = null;
for (String sdPath : path) {
Bitmap bitmapOrg = BitmapFactory.decodeFile(sdPath);
ByteArrayOutputStream bao = new ByteArrayOutputStream();
//Resize the image
double width = bitmapOrg.getWidth();
double height = bitmapOrg.getHeight();
double ratio = 400/width;
int newheight = (int)(ratio*height);
System.out.println("———-width" + width);
System.out.println("———-height" + height);
bitmapOrg = Bitmap.createScaledBitmap(bitmapOrg, 400, newheight, true);
//Here you can define .PNG as well
bitmapOrg.compress(Bitmap.CompressFormat.JPEG, 95, bao);
byte[] ba = bao.toByteArray();
String ba1 = Base64.encodeToString(ba, Base64.NO_WRAP);
System.out.println("uploading image now ——–" + ba1);
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("image", ba1));
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://proopt.co.za.winhost.wa.co.za/api/Files/");
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
// print response
outPut = EntityUtils.toString(entity);
Log.i("GET RESPONSE—-", outPut);
//is = entity.getContent();
Log.e("log_tag ******", "good connection");
bitmapOrg.recycle();
} catch (Exception e) {
Log.e("log_tag ******", "Error in http connection " + e.toString());
}
}
return outPut;
}
}
}
And my asp.net controller class is:
public class FilesController : ApiController
{
// GET api/files
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/files/5
public string Get(int id)
{
return "value";
}
// POST api/files
public HttpResponseMessage Post(string filename)
{
var task = this.Request.Content.ReadAsStreamAsync();
task.Wait();
Stream requestStream = task.Result;
try
{
Stream fileStream = File.Create(HttpContext.Current.Server.MapPath("~/" + filename));
requestStream.CopyTo(fileStream);
fileStream.Close();
requestStream.Close();
}
catch (IOException)
{
// throw new HttpResponseException("A generic error occured. Please try again later.", HttpStatusCode.InternalServerError);
}
HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.Created;
return response;
}
// PUT api/files/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/files/5
public void Delete(int id)
{
}
}
Here is global.asax.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace WSISWebService
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{filename}",
defaults: new { filename = RouteParameter.Optional }
);
}
}
}
I am pretty desperate to get this working, can anybody assist me??
UPDATE: I have managed to "find" the post method, but I just can't seem to get android to send the file, can somebody please help me code the server side post method???
Upvotes: 1
Views: 1118
Reputation: 1173
The URL your posting to is finding its way to your controller class because your default route mappings are mapping "/api/Files/" to the FilesController. However, your URI doesn't identify an "action", e.g. "/api/Files/Post".
BTW, the default routes in an ASP.NET MVC application are "/{controller}/{action}" and usually have defaults "controller=Default" and "action=Index". So, by default, "/api/Files" would be looking for an action method named "Index", but your FilesController has no such method.
Short answers:
Long answer:
Now that you've posted your Global.asax.cs, let's examine the one route template you have:
"{controller}/{filename}"
You're telling MVC that when you have a URL that looks like "/something/somethingelse" to take the "something" and use that as the controller name, and the "somethingelse" as the filename. Nothing in your route identifies which action to invoke.
Now that's OK if you don't want any other actions to be invoked. In that case, you can hard-code the action "Post" in the defaults for the route:
defaults: new { filename = RouteParameter.Optional, action = "Post" }
If you want to be able to invoke the other action methods you have (e.g. Put, Delete, etc.), then you can modify your URL patterns (and calling URL) to include "action":
"{controller}/{action}/{filename}" // E.g. url: "/Files/Post/myfilename.txt"
Or, maybe you really want everything to always go through the FilesController in which case you can set the controller in the defaults and have the route template just expect the action and filename (e.g. .../Post/myfilename.txt):
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{action}/{filename}",
defaults: new { filename = RouteParameter.Optional, controller = "Files"}
);
Upvotes: 1