Reputation: 844
i am trying to upload a large file from android to wcf rest service. i am able to upload the file properly. but my problem is when the connection fails while uploading the file from android to server i am not able to resume the upload process. again the upload is starting from beginning can any one kindly tell me the way to write a wcf rest service where i can resume my upload process.
i am currently using the following rest service to upload data:
public string PostImage(Stream stream)
{
try
{
byte[] buff = new byte[50485760];
using (FileStream fs = new FileStream(System.Web.HttpContext.Current.Server.MapPath(@"~/Images/" + "filename"), FileMode.Create))
{
int bytesRead = stream.Read(buff, 0, buff.Length);
while (bytesRead > 0)
{
fs.Write(buff, 0, bytesRead);
bytesRead = stream.Read(buff, 0, buff.Length);
}
}
}
catch (Exception e)
{
}
//Class1 parser = new Class1(stream);
return "Recieved the image on server";
}
how can i modify the rest service to provide a facility to upload the file where it has stopped. how can i add Range and Content Range headers to provide this information to the client. if there is any sample code for resume upload process please provide me the code.
Upvotes: 0
Views: 810
Reputation: 3713
I've just implemented something like this. If the upload fails or only half the bytes arrive at the host, what do you do?
Our approach was to create two additional methods, one that the client device calls to check the status of the prior upload (called "GetStatusOfLastUpload(SessionID, DeviceID, FileID)") and another called ResumeUpload(SessionID, DeviceID, FileID, MD5Hash, Bytes).
The function GetStatusOfLastUpload does what the name implies, sending an additional request to the host to check the status (stored on a SQL database) of a prior upload.
Key to this process is using a calculated MD5 Hash and byte counts to determine on the host side if the complete file was delivered. For example, if the client sends 1MB, but the Host only gets 500KB and the MD5 hash sent from the client does not match the MD5 calculated by the host based on the bytes received, we set the status of the upload data record to "Imcomplete" and store the byte count. So when GetStatusOfLastUpload is called, the Host returns (if necessary) that the last upload was "Imcomplete" and that the byte count received was just 500KB.
The client app then takes this information from GetStatusOfLastUpload and calls "ResumeUpload", sending an MD5 hash of the complete file and the remaining bytes that the host needs. The host again, on receiving this, combines the bytes and calculates the MD5 hash, comparing that to the value sent by the client. If the hash values match, you know you have a complete file, if not, repeat the process.
There's no way, here, to include all the sample code, but I'll say that our ResumeUpload function looks identical to our Upload function, but includes a few extra checks to ensure everything is correct. ______________________edit__________________________
We use .NET stuff on the client side, but this is some sample Java code (better for Android) I put togeter for those developing on non Windows devices. It constructs the message that's exchanged with the WCF host (also .NET).
public MsgStruct CallHost(String URL, String Content, byte[] outBytes){
String textValue="";
byte[] inBytes =new byte[] {0};
try {
// Creating a new empty SOAP message object
SOAPMessage reqMsg = MessageFactory.newInstance().createMessage();
// Populating SOAP body
SOAPEnvelope envelope = reqMsg.getSOAPPart().getEnvelope();
SOAPBody body = envelope.getBody();
SOAPBodyElement service = body.addBodyElement(envelope.createName("HostConnect", "", WCFNameSpace)); //good here
SOAPElement paramInMsg = service.addChildElement(envelope.createName("inMsg", "", "")); //good here
//SOAPElement paramBodySection = paramInMsg.addChildElement(envelope.createName("BodySection", "", DataContractNameSpace));
SOAPElement paramTextSection = paramInMsg.addChildElement(envelope.createName("TextSection", "", DataContractNameSpace));
SOAPElement paramBodySection = paramInMsg.addChildElement(envelope.createName("BodySection", "", DataContractNameSpace));
//get the byte array to send and populate the fields
String sOut=org.apache.commons.codec.binary.Base64.encodeBase64String(outBytes);
paramBodySection.addTextNode(sOut); //adding the binary stuff here "AA=="
paramTextSection.addTextNode(Content); //adding the text content here
envelope.removeAttribute("Header");
envelope.setPrefix("s");
envelope.getBody().setPrefix("s");
// Setting SOAPAction header line
MimeHeaders headers = reqMsg.getMimeHeaders();
headers.addHeader("SOAPAction", SoapAction); //good here
headers.setHeader("Content-Type", "text/xml; charset=utf-8"); //good here
headers.setHeader("Host", "Localhost:8085"); //good here
headers.setHeader("Expect", "100-continue");
// Connecting and Calling
SOAPConnection con = SOAPConnectionFactory.newInstance().createConnection();
SOAPMessage resMsg = con.call(reqMsg, URL);
resMsg.saveChanges();
con.close();
//check the host response
if (resMsg != null){
try{
SOAPBody responseBody = resMsg.getSOAPBody();
SOAPBodyElement responseElement0= (SOAPBodyElement)responseBody.getChildElements().next();
SOAPElement responseElement1 = (SOAPElement)responseElement0.getChildElements().next();
SOAPElement TextElement = (SOAPElement)responseElement1.getFirstChild();
SOAPElement bodyElement = (SOAPElement)responseElement1.getLastChild();
Node nodeBody = (Node)bodyElement;
inBytes = getBytesFromDoc(nodeBody);
textValue = TextElement.getTextContent();
}catch (SOAPException se){
String smessage = se.getMessage();
}
return new MsgStruct(textValue,inBytes);
//no response from host
}else{
Debug.println("error","Error- nothign found");
return null;
}
} catch (Exception e) {
Debug.println("error",e.getMessage());
return null;
}
}
On the host side, there's just too much code to share, but the concept is in the posting above. The thing to keep in mind is that WCF is (almost) always a Client Request/Host Response scenario, so the client device needs to initiate everything, including a check to see if the last upload was successful. Follow what I'm suggesting?
This might be a good source of information for you: http://fszlin.dymetis.com/post/2010/05/10/Comsuming-WCF-Services-With-Android.aspx
Upvotes: 1