Wcf Service to Accept a Post Encoded Multipart/Form-Data

WCF service to accept a post encoded multipart/form-data

So, here goes...

Create your service contract which an operation which accepts a stream for its only parameter, decorate with WebInvoke as below

[ServiceContract]
public interface IService1 {

[OperationContract]
[WebInvoke(
Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/Upload")]
void Upload(Stream data);

}

Create the class...

    public class Service1 : IService1 {

public void Upload(Stream data) {

// Get header info from WebOperationContext.Current.IncomingRequest.Headers
// open and decode the multipart data, save to the desired place
}

And the config, to accept streamed data, and the maximum size

<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="WebConfiguration"
maxBufferSize="65536"
maxReceivedMessageSize="2000000000"
transferMode="Streamed">
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Sandbox.WCFUpload.Web.Service1Behavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Sandbox.WCFUpload.Web.Service1" behaviorConfiguration="Sandbox.WCFUpload.Web.Service1Behavior">
<endpoint
address=""
binding="webHttpBinding"
behaviorConfiguration="WebBehavior"
bindingConfiguration="WebConfiguration"
contract="Sandbox.WCFUpload.Web.IService1" />
</service>
</services>
</system.serviceModel>

Also in the System.Web increase the amount of data allowed in System.Web

<system.web>
<otherStuff>...</otherStuff>
<httpRuntime maxRequestLength="2000000"/>
</system.web>

This is just the basics, but allows for the addition of a Progress method to show an ajax progress bar and you may want to add some security.

POSTing multipart/form-data to a WCF REST service: the action changes

Did you see this question? WCF service to accept a post encoded multipart/form-data

WCF service to consume Multipart data from android

Ok so I had solved this issue long back with some try and error basis, but since no one replied I am posting my solution.

public class MultipartParser
{
public IDictionary<string, string> Parameters = new Dictionary<string, string>();
public MultipartParser(Stream stream)
{
this.Parse(stream, Encoding.UTF8);
}
public MultipartParser(Stream stream, Encoding encoding)
{
this.Parse(stream, encoding);
}
public string getcontent(Stream stream, Encoding encoding)
{
// Read the stream into a byte array
byte[] data = ToByteArray(stream);
// Copy to a string for header parsing
string content = encoding.GetString(data);
string delimiter = content.Substring(0, content.IndexOf("\r\n"));
string[] sections = content.Split(new string[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
foreach (string s in sections)
{
Match nameMatch = new Regex(@"(?<=name\=\"")(.*?)(?=\"")").Match(s);
string name = nameMatch.Value.Trim().ToLower();
if (!string.IsNullOrWhiteSpace(name))
{
int startIndex = nameMatch.Index + nameMatch.Length + "\r\n\r\n".Length;
}
}
string strRet = ""; //Parameters["name"];
return strRet;
}
private void Parse(Stream stream, Encoding encoding)
{
this.Success = false;
byte[] data = ToByteArray(stream);
string content = encoding.GetString(data);

string s_no = content.Substring(content.IndexOf("s_no"), 100);
string[] lines = s_no.Split(new[] { "\r\n", "\r\n\r\n" }, StringSplitOptions.None);
this.S_NO = lines[3];

string count = content.Substring(content.IndexOf("Count"), 100);
string[] linescount = count.Split(new[] { "\r\n", "\r\n\r\n" }, StringSplitOptions.None);
this.Count = linescount[3];

int delimiterEndIndex = content.IndexOf("\r\n");
if (delimiterEndIndex > -1)
{
string delimiter = content.Substring(0, content.IndexOf("\r\n"));
// Look for Content-Type
Regex re = new Regex(@"(?<=Content\-Type:)(.*?)");
Match contentTypeMatch = re.Match(content);
// Look for filename
re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")");
Match filenameMatch = re.Match(content);
re = new Regex(@"(?<=Content\-Length:)(.*)");
Match contentLenMatch = re.Match(content);

if (contentLenMatch.Success)
{
contentLenMatch = contentLenMatch.NextMatch();
}
if (contentLenMatch.Success)
{
contentLenMatch = contentLenMatch.NextMatch();
}
//re = new Regex(@"(?<=name\=\"")(.*?)(?=\"")");
//Match nameMatch = re.Match(content);
// Did we find the required values?
if (contentTypeMatch.Success && filenameMatch.Success)
{
// Set properties
this.ContentType = contentTypeMatch.Value.Trim();
this.Filename = filenameMatch.Value.Trim();
// Get the start & end indexes of the file contents
int startIndex = contentLenMatch.Index + contentLenMatch.Length + "\r\n".Length + 1;
byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
int endIndex = IndexOf(data, delimiterBytes, startIndex);
int contentLength = endIndex - startIndex;
// Extract the file contents from the byte array
byte[] fileData = new byte[contentLength];
Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength);
this.FileContents = fileData;
this.Success = true;
}
}
}
private int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex)
{
int index = 0;
int startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex);
if (startPos != -1)
{
while ((startPos + index) < searchWithin.Length)
{
if (searchWithin[startPos + index] == serachFor[index])
{
index++;
if (index == serachFor.Length)
{
return startPos;
}
}
else
{
startPos = Array.IndexOf<byte>(searchWithin, serachFor[0], startPos + index);
if (startPos == -1)
{
return -1;
}
index = 0;
}
}
}
return -1;
}
private byte[] ToByteArray(Stream stream)
{
byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
public bool Success
{
get;
private set;
}
public string ContentType
{
get;
private set;
}
public string Filename
{
get;
private set;
}
public byte[] FileContents
{
get;
private set;
}
public string Imgname
{
get;
private set;
}
public string S_NO
{
get;
private set;
}
public string Count
{
get;
private set;
}
}
// End of Wcf rest Service Code

my Android code is as shown below using Okhttp

public static ResultVO uploadIMGMultipart(String s_no, int noOfDocs, String filepath, String url) {
ResultVO resultVO = new ResultVO();
OkHttpClient client = new OkHttpClient().newBuilder()
.retryOnConnectionFailure(false)
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.build();

RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("s_no",s_no.toString())
.addFormDataPart("Count", ""+noOfDocs)
.addFormDataPart("filestream", new File(filepath).getName().toString(), RequestBody.create(MEDIA_TYPE_PNG, new File(filepath)))
.build();

Request request = new Request.Builder()
.url(url)
.addHeader("content-type", "multipart/form-data;")
.cacheControl(CacheControl.FORCE_NETWORK)
.post(requestBody)
.build();

Response response = null;
try {
response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

resultVO.setStatus(response.code());
resultVO.setResult(response.body().string());
} catch (Exception e) {
e.printStackTrace();
}

return resultVO;
}

Call the above method in AsyncTask from your class thats it....

Uploading file from Flex to WCF REST Stream issues (how to decode multipart form post in REST WS)

Thanks to Anthony over at http://antscode.blogspot.com/ for the multipart parser that works great (for images, txt files, etc).

http://antscode.blogspot.com/2009/11/parsing-multipart-form-data-in-wcf.html

Upload file from Html form (multipart/form-data) to WCF REST service as a stream without streaming the whole form's inputs?

Please find some code that might help you to pass a file along with its details in a single call to the REST service:

First you would need something called a MultipartParser as shown below:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace SampleService
{
public class MultipartParser
{
private byte[] requestData;
public MultipartParser(Stream stream)
{
this.Parse(stream, Encoding.UTF8);
ParseParameter(stream, Encoding.UTF8);
}

public MultipartParser(Stream stream, Encoding encoding)
{
this.Parse(stream, encoding);
}

private void Parse(Stream stream, Encoding encoding)
{
this.Success = false;

// Read the stream into a byte array
byte[] data = ToByteArray(stream);
requestData = data;

// Copy to a string for header parsing
string content = encoding.GetString(data);

// The first line should contain the delimiter
int delimiterEndIndex = content.IndexOf("\r\n");

if (delimiterEndIndex > -1)
{
string delimiter = content.Substring(0, content.IndexOf("\r\n"));

// Look for Content-Type
Regex re = new Regex(@"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)");
Match contentTypeMatch = re.Match(content);

// Look for filename
re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")");
Match filenameMatch = re.Match(content);

// Did we find the required values?
if (contentTypeMatch.Success && filenameMatch.Success)
{
// Set properties
this.ContentType = contentTypeMatch.Value.Trim();
this.Filename = filenameMatch.Value.Trim();

// Get the start & end indexes of the file contents
int startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length;

byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
int endIndex = IndexOf(data, delimiterBytes, startIndex);

int contentLength = endIndex - startIndex;

// Extract the file contents from the byte array
byte[] fileData = new byte[contentLength];

Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength);

this.FileContents = fileData;
this.Success = true;
}
}
}

private void ParseParameter(Stream stream, Encoding encoding)
{
this.Success = false;

// Read the stream into a byte array
byte[] data;
if (requestData.Length == 0)
{
data = ToByteArray(stream);
}
else { data = requestData; }
// Copy to a string for header parsing
string content = encoding.GetString(data);

// The first line should contain the delimiter
int delimiterEndIndex = content.IndexOf("\r\n");

if (delimiterEndIndex > -1)
{
string delimiter = content.Substring(0, content.IndexOf("\r\n"));
string[] splitContents = content.Split(new[] {delimiter}, StringSplitOptions.RemoveEmptyEntries);
foreach (string t in splitContents)
{
// Look for Content-Type
Regex contentTypeRegex = new Regex(@"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)");
Match contentTypeMatch = contentTypeRegex.Match(t);

// Look for name of parameter
Regex re = new Regex(@"(?<=name\=\"")(.*)");
Match name = re.Match(t);

// Look for filename
re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")");
Match filenameMatch = re.Match(t);

// Did we find the required values?
if (name.Success || filenameMatch.Success)
{
// Set properties
//this.ContentType = name.Value.Trim();
int startIndex;
if (filenameMatch.Success)
{
this.Filename = filenameMatch.Value.Trim();
}
if(contentTypeMatch.Success)
{
// Get the start & end indexes of the file contents
startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length;
}
else
{
startIndex = name.Index + name.Length + "\r\n\r\n".Length;
}

//byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
//int endIndex = IndexOf(data, delimiterBytes, startIndex);

//int contentLength = t.Length - startIndex;
string propertyData = t.Substring(startIndex - 1, t.Length - startIndex);
// Extract the file contents from the byte array
//byte[] paramData = new byte[contentLength];

//Buffer.BlockCopy(data, startIndex, paramData, 0, contentLength);

MyContent myContent = new MyContent();
myContent.Data = encoding.GetBytes(propertyData);
myContent.StringData = propertyData;
myContent.PropertyName = name.Value.Trim();

if (MyContents == null)
MyContents = new List<MyContent>();

MyContents.Add(myContent);
this.Success = true;
}
}
}
}

private int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex)
{
int index = 0;
int startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex);

if (startPos != -1)
{
while ((startPos + index) < searchWithin.Length)
{
if (searchWithin[startPos + index] == serachFor[index])
{
index++;
if (index == serachFor.Length)
{
return startPos;
}
}
else
{
startPos = Array.IndexOf<byte>(searchWithin, serachFor[0], startPos + index);
if (startPos == -1)
{
return -1;
}
index = 0;
}
}
}

return -1;
}

private byte[] ToByteArray(Stream stream)
{
byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}

public List<MyContent> MyContents { get; set; }

public bool Success
{
get;
private set;
}

public string ContentType
{
get;
private set;
}

public string Filename
{
get;
private set;
}

public byte[] FileContents
{
get;
private set;
}
}

public class MyContent
{
public byte[] Data { get; set; }
public string PropertyName { get; set; }
public string StringData { get; set; }
}
}

Now define your REST method as shown:

[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest)]
AttachmentRequestDto AddAttachmentToNote(Stream stream);

Now the implemenation of the above method as shown:

public AttachmentRequestDto AddAttachmentToNote(Stream stream)
{
MultipartParser parser = new MultipartParser(stream);
if(parser != null && parser.Success)
{
foreach (var content in parser.MyContents)
{
// Observe your string here which is a serialized version of your file or the object being passed. Based on the string do the necessary action.
string str = Encoding.UTF8.GetString(content.Data);

}
}

return new AttachmentRequestDto();
}

My AttachmentRequestDto looks as shown:

[DataContract]
public class AttachmentRequestDto
{
[DataMember]
public string Title { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
public string Filename { get; set; }
}

Now from the client i do perform a POST as shown :

Image image = Image.FromFile("C:\\Users\\Guest\\Desktop\\sample.png");
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
byte[] imageArray = ms.ToArray();
ms.Close();

AttachmentRequestDto objAttachmentRequestDto = new AttachmentRequestDto();
objAttachmentRequestDto.Title = "Sample";
objAttachmentRequestDto.Description = "Sample book";
objAttachmentRequestDto.FileName = "SampleBook.png";

var serializer = new DataContractSerializer(typeof(AttachmentRequestDto));
var ms = new MemoryStream();
serializer.WriteObject(ms, objAttachmentRequestDto);
ms.Position = 0;
var reader = new StreamReader(ms);
string requestBody = reader.ReadToEnd();

var client = new RestClient();
client.BaseUrl = "http://localhost/SampleService/Service1.svc";
var request = new RestRequest(method) { DateFormat = DataFormat.Xml.ToString(), Resource = resourceUrl };
if(requestBody !=null)
request.AddParameter("objAttachmentRequestDto", requestBody);
request.AddFile("stream", image, "Array.png");
var response = client.Execute(request);

I do use a third party dll for the above code called RESTSharp.

Once on the server the MultipartParser identifies your request boundary to split the necessary contents and then you can decide on what to do with each of the split contents (save to file,db, etc..)

Just make sure you have the approriate config entries for the above as well along with dataContractSerializer property and readerQuotas set for the webHttpBinding.

NOTE: The MultipartParser can be more re-factored if needed.

Reading file input from a multipart/form-data POST

You may take a look at the following blog post which illustrates a technique that could be used to parse multipart/form-data on the server using the Multipart Parser:

public void Upload(Stream stream)
{
MultipartParser parser = new MultipartParser(stream);
if (parser.Success)
{
// Save the file
SaveFile(parser.Filename, parser.ContentType, parser.FileContents);
}
}

Another possibility is to enable aspnet compatibility and use HttpContext.Current.Request but that's not a very WCFish way.

Getting POST data from form with WCF Service Application

I found this other_wcf_stuff. All trick is in web.config (there must be declared binding (only webHttpBinding) and behavior configuration). Also my interface of service is now:


[ServiceContract]
public interface IService
{
[OperationContract]
string GetData(Stream aoInput);
}


Related Topics



Leave a reply



Submit