Reputation: 79
I'm trying to upload a file from Android to a WCF Service. I've gotten to the point where the method on the WCF is triggered and the file saves. When trying to open the file, I get an error message stating that it's been corrupted.
I think the error might be in the HttpFileUpload class that one of the static variables are incorrect or that what I'm sending up isn't a stream but instead something else that the service converted to a stream somehow thus the corruption.
The code used can be found below.
Android Code:
HttpFileUpload Class:
The HttpFileUpload code found here:
private static final String LINE_FEED = "\r\n";
private String charset = "UTF-8";
public HttpFileUpload(String requestURL) throws IOException {
// creates a unique boundary based on time stamp
boundary = "===" + System.currentTimeMillis() + "===";
URL url = new URL(requestURL);
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setUseCaches(false);
httpConn.setDoOutput(true); // indicates POST method
httpConn.setDoInput(true);
httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
httpConn.setRequestProperty("Host", "x.x.x.x:x");
outputStream = httpConn.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true);
}
public void addFilePart(String fieldName, File uploadFile) throws IOException {
String fileName = uploadFile.getName();
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); // form-data
writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
FileInputStream inputStream = new FileInputStream(uploadFile);
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
inputStream.close();
writer.append(LINE_FEED);
writer.flush();
}
public List<String> finish() throws IOException {
List<String> response = new ArrayList<String>();
writer.append(LINE_FEED).flush();
writer.append("--" + boundary + "--").append(LINE_FEED);
writer.close();
int status = httpConn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
response.add(line);
}
reader.close();
httpConn.disconnect();
} else {
throw new IOException("Server returned status: " + status);
}
return response;
}
Service Connector Class
String urlTo = LOCATION + "/UploadUserProfilePicture";
File file = new File(imagePath);
try {
HttpFileUpload httpFileUpload = new HttpFileUpload(urlTo);
httpFileUpload.addFilePart("image", file);
List<String> responses = httpFileUpload.finish();
for (String line : responses) {
System.out.println(line);
}
return responses.get(0);
} catch (Exception ex) {
return ex.toString();
}
WCF Service Code
IMobileService:
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Result UploadUserProfilePicture(Stream image);
MobileService:
The FileType fileType = bytes.GetFileType()
is a plugin that returns the extension. Currently the plugin does not work as the fileType is always null.
However the image variable might already be corrupted.
public Result UploadUserProfilePicture(Stream image)
{
try
{
var bytes = CommonMethods.ReadToEnd(image);
FileType fileType = bytes.GetFileType();
Guid guid = Guid.NewGuid();
string imageName = guid.ToString() + "." + fileType.Extension;
var buf = new byte[1024];
var path = Path.Combine(@"C:\fileUpload\" + imageName); //" ocd
File.WriteAllBytes(path, bytes);
return new Result
{
Success = true,
Message = imageName
};
}
catch(Exception ex)
{
return new Result
{
Success = false,
Message = ex.ToString()
};
}
}
Upvotes: 1
Views: 1223
Reputation: 79
Thanks Thorsten for leading me in the right direction. Here is a coded example:
Android Code:
ImageUploading Activity:
File initialFile = new File(imagePath);
byte[] bytes = FileUtils.readFileToByteArray(initialFile);
final String base64 = android.util.Base64.encodeToString(bytes, android.util.Base64.DEFAULT);
Thread uploadFileThread = new Thread(new Runnable() {
@Override
public void run() {
FileToUpload fileToUpload = new FileToUpload();
fileToUpload.setFile(base64);
String[] result = ServiceConnector.UploadUserProfilePicture(fileToUpload);
}
});
ServiceConnector Class:
public static String[] UploadUserProfilePicture(FileToUpload fileToUpload) {
StringBuilder sb = new StringBuilder();
String result[] = {"false", "Something went wrong"};
HttpURLConnection urlConnection = null;
try {
URL url;
DataOutputStream printout;
DataInputStream input;
url = new URL(LOCATION + "/UploadUserProfilePicture");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Host", "x.x.x.x:xxxx");
urlConnection.setConnectTimeout(1*1000*3600);
urlConnection.setReadTimeout(1*1000*3600);
urlConnection.connect();
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
// Send POST output.
printout = new DataOutputStream(urlConnection.getOutputStream());
byte[] extraInfo = gson.toJson(fileToUpload).getBytes("UTF-8");
printout.write(extraInfo);
printout.flush();
printout.close();
int HttpResult = urlConnection.getResponseCode();
if (HttpResult == HttpURLConnection.HTTP_OK) {
BufferedReader br = new BufferedReader(new InputStreamReader(
urlConnection.getInputStream(), "utf-8"));
String line = null;
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
br.close();
result = CommonMethods.parseJsonToArray(sb.toString());
} else {
System.out.println("*** RESPONSE MESSAGE ***");
System.out.println(urlConnection.getResponseMessage());
}
return result;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null)
urlConnection.disconnect();
}
return null;
}
C# Service Code:
Service:
public Result UploadUserProfilePicture(FileToUpload image)
{
try
{
byte[] bytes = Convert.FromBase64String(image.File);
FileType fileType = bytes.GetFileType();
Guid guid = Guid.NewGuid();
string imageName = guid.ToString() + "." + fileType.Extension;
var path = Path.Combine(@"C:\UploadedImages\" + imageName);
File.WriteAllBytes(path, bytes);
return new Result
{
Success = true,
Message = imageName
};
}
catch(Exception ex)
{
return new Result
{
Success = false,
Message = ex.ToString()
};
}
}
IService:
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Result UploadUserProfilePicture(FileToUpload image);
Web.Config on Service:
I got a 400 Bad request error without the below.
<bindings>
<webHttpBinding>
<binding
name="binding"
openTimeout="00:10:00" closeTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647"
maxBufferSize="2147483647" transferMode="Streamed">
<readerQuotas maxDepth="250000000" maxStringContentLength="250000000" maxArrayLength="250000000" maxBytesPerRead="250000000" maxNameTableCharCount="250000000"/>
<security mode="None" />
</binding>
</webHttpBinding>
</bindings>
Upvotes: 0
Reputation: 56727
Without having ever tried this in combination Android/WCF REST there are a few things that I notice in your code.
First of all, you say in the operation contract that your REST method expects JSON and returns JSON. But the Android code is not sending JSON. It just sends the content of the input file as is. That's one thing I consider incorrect. The code you're using is good if you want to upload something to a web site the same way a browser form would, but that's not the same as using a REST request.
The other thing is: Your REST method would not take a Stream
parameter, as it doesn't get a stream.
The first thing I'd do is design the REST POST method so that it accepts a JSON object like this:
{ imageBytes = "0dac...." }
with the imageBytes
being a base64 encoded version of the image you want to save (for example: the bytes of a PNG file base64 encoded). You can then use other means to test whether that works well.
Then I'd change the Android code so that it
Then things should work out. As I said above I've not done that myself before in that combination, so there's no sample code I can give you.
Upvotes: 1