Getting Byte[] from Decoded base64 MIME Part - mimekit

We receive files from various entities that can contain MIME parts (which are PDFs) in them that are base64 encoded. We extract it out and store it to open later. This has worked fine for all but from one of the entities. Their files will save, but have not been able to open them.
Here is the code:
public byte[] GetAttachmentByName(string name)
{
foreach (var attachment in _mimeMessage.BodyParts.OfType<MimePart>())
{
if (attachment.ContentId != name)
continue;
using (var stream = new System.IO.MemoryStream())
{
using (var filtered = new FilteredStream(stream))
{
filtered.Add(DecoderFilter.Create("base64"));
attachment.ContentObject.DecodeTo(filtered);
return stream.ToArray();
}
}
}
return null;}
Here is what part of the MIME looks like:
MIME-Version: 1.0
Content-Type: multipart/related; boundary="=-antAW7IrKfe1DvH3559M9g=="
…
--=-antAW7IrKfe1DvH3559M9g==
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Description:
Content-Id: "AC13127925.pdf"
…
bGVyDTw8IA0vU2l6ZSAxODkgDS9Sb290IDE4OCAwIFIgDS9JbmZvIDEgMCBSIA0+PiANc3Rh
cnR4cmVmDTU3MzU3MiANJSVFT0YN
--=-antAW7IrKfe1DvH3559M9g==--
(I included the start, middle, and end of the MIME part. There is also XML in another part of the file, but that is processing just fine. The problem we are having is with the MIME/PDF.)
Any guidance at all is appreciated.

The MimeContent.DecodeTo() method already decodes the base64 for you, so you need to change your code to this:
public byte[] GetAttachmentByName(string name)
{
foreach (var attachment in _mimeMessage.BodyParts.OfType<MimePart>())
{
if (attachment.ContentId != name)
continue;
using (var stream = new System.IO.MemoryStream())
{
attachment.ContentObject.DecodeTo(filtered);
return stream.ToArray();
}
}
return null;
}

Related

How to return multipart content with html and xml

I have to re-write a web service in .NET Core 2.2, and the service must return a multipart response with XML and base-64 encoded HTML. The current service is a really old java program with no documentation.
The request comes from an old service that can't change and must receive HTML 4.01 as the response. An actual sample is shown below.
What kind of response object do I return?
How do I combine the XML and encoded HTML together into one response object?
I don't expect anyone to write my code for me but I would appreciate some help getting started.
Sample response
--75df5969-8400-11d5-c000-0021ffffff97
Content-type: text/xml; charset="UTF-8"
Content-id: XMLROOT
<?xml version="1.0" encoding="UTF-8"?><XMLROOT><Service><StatusCd>0</StatusCd></Service></XMLROOT>
--75df5969-8400-11d5-c000-0021ffffff97
Content-type: application/html
Content-id: Inquiry.html
Content-description: Response to View Inquiry
PCFET0NUWVBFIEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMDEgVHJhbnNpdGlvbmFsLy9FTiI+Cgo8SFRNTD4KPEhFQUQ+CjxNRVRBIGh0dHAtZXF1aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PUlTTy04ODU5LTEiPgo8TUVUQSBodHRwLWVxdWl2PSJDb250ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KCgk8U1RZTEUgVFlQRT0idGV4dC9jc3MiPgoJCVRSIHsgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmV5IH0KCQkudGREYXRhIHsKCSAJCWZvbnQtc2l6ZTo4cHQ7CgkJCWNvbG9yOiMwMDAwOUM7CgkJCWZvbnQtZmFtaWx5OlZlcmRhbmEsIEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7IH0KCQkudGRMYWJlbCB7CgkgCQlmb250LXNpemU6OHB0OwoJCQlmb250LXdlaWdodDpib2xkZXI7CgkJCWZvbnQtZmFtaWx5OlZlcmRhbmEsIEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7IH0KCQkudGRIZWFkIHsKCQkJYmFja2dyb3VuZC1jb2xvcjojODA4MDgwOwoJCQl0ZXh0LWFsaWduOmNlbnRlcjsKCSAJCWZvbnQtc2l6ZTo4cHQ7CgkJCWZvbnQtd2VpZ2h0OmJvbGRlcjsKCQkJZm9udC1mYW1pbHk6VmVyZGFuYSwgQXJpYWwsIEhlbHZldGljYSwgc2Fucy1zZXJpZjsgfQoJPC9TVFlMRT4KCjxUSVRMRT5JbnF1aXJ5PC9USVRMRT4KPC9IRUFEPgo8Qk9EWT4KPHRhYmxlIEJPUkRFUj0iMCIgQ0VMTFBBRERJTkc9IjAiIENFTExTUEFDSU5HPSIwIiBXSURUSD0iNzAwIj4KCTx0cj4gCgkJPHRkIEJHQ09MT1I9IiNmZmZmZmYiPgoJCQk8VEFCTEUgQk9SREVSPSIxIiBDRUxMUEFERElORz0iMiIgQ0VMTFNQQUNJTkc9IjEiIFdJRFRIPSI3MDAiPgoJCQkJPFRSPjxURCBDT0xTUEFOPSI0IiBBTElHTj0iY2VudGVyIiBCR0NPTE9SPSIjZmZmZmZmIj4KCQkJCQlUZXN0IG1lc3NhZ2UgCgkJCQk8L1REPjwvVFI+CgkJCTwvVEFCTEU+CQoJCTx0ZD48L3RkPgoJPHRyPjwvdHI+CjwvdGFibGU+Cgo8L0JPRFk+CjwvSFRNTD4=
--75df5969-8400-11d5-c000-0021ffffff97--
What kind of response object do I return? How do I combine the XML and encoded HTML together into one response object?
There's no such a built-in result type. However, it would be easy to create a custom MultipartResult that will contains the XML + HTML (encoded). For example:
public class XmlAndEncodedHtmlMultipartResult : IActionResult
{
private StringContent _xmlContent;
private StringContent _htmlContent;
private MultipartContent _multipartContent=new MultipartContent(Subtype); // the final content
public static string Subtype = "my-xml+html" ; // `content-type: multipart/{Subtype}`
public XmlAndEncodedHtmlMultipartResult SetXmlContent(string xml, string contentId = "XMLROOT")
{
var xmlContent = new StringContent(xml, Encoding.UTF8, "text/xml");
xmlContent.Headers.Add("Content-id", contentId);
this._xmlContent = xmlContent;
return this;
}
public XmlAndEncodedHtmlMultipartResult SetEncodedHtmlContent(string rawHtml,string contentId="Inquiry.html", string description="Response to View Inquiry")
{
var bytes= Encoding.UTF8.GetBytes(rawHtml);
var encodedHtml = Convert.ToBase64String(bytes);
var htmlContent = new StringContent(encodedHtml, Encoding.UTF8, "application/html");
htmlContent.Headers.Add("Content-id", contentId);
htmlContent.Headers.Add("Content-description",description);
this._htmlContent = htmlContent;
return this;
}
public async Task ExecuteResultAsync(ActionContext context)
{
if(this._htmlContent ==null || this._xmlContent ==null){ throw new Exception("html content & xml must be null");}
this._multipartContent.Add(this._xmlContent);
this._multipartContent.Add(this._htmlContent);
var response = context.HttpContext.Response;
response.ContentType = this._multipartContent.Headers.ContentType.ToString();
// ... custom other headers as you like
await _multipartContent.CopyToAsync(response.Body);
}
}
In your action, just invoke it as below :
public IActionResult Privacy()
{
var xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><XMLROOT><Service><StatusCd>0</StatusCd></Service></XMLROOT>";
var html = "<html><head></head><body>hello,world</body></html>";
return new XmlAndEncodedHtmlMultipartResult().SetEncodedHtmlContent(html).SetXmlContent(xml);
}

How to convert a file to bytes and bytes to a file in mvc4

I am using MVC4. My requirement is:
I have to convert the file into byte array and save to database varbinary column.
For this I written code like below:
public byte[] Doc { get; set; }
Document.Doc = GetFilesBytes(PostedFile);
public static byte[] GetFilesBytes(HttpPostedFileBase file)
{
MemoryStream target = new MemoryStream();
file.InputStream.CopyTo(target);
return target.ToArray();
}
I am downloading the file by using the following code:
public ActionResult Download(int id)
{
List<Document> Documents = new List<Document>();
using (SchedulingServiceInstanceManager facade = new SchedulingServiceInstanceManager("SchedulingServiceWsHttpEndPoint"))
{
Document Document = new Document();
Document.DMLType = Constant.DMLTYPE_SELECT;
Documents = facade.GetDocuments(Document);
}
var file = Documents.FirstOrDefault(f => f.DocumentID == id);
return File(file.Doc.ToArray(), "application/octet-stream", file.Name);
}
when I am downloading pdf file then it is showing message as "There was an error opening this document. The file is damaged and could not be repaired."
Any thing else I need to do?
I tried with the following code but no luck
return File(file.Doc.ToArray(), "application/pdf", file.Name);
Please help me to solve the issue.
Thanks in advance.
Please try as in below code in your controller
FileStream stream = File.OpenRead(#"c:\path\to\your\file\here.txt");
byte[] fileBytes= new byte[stream.Length];
stream.Read(fileBytes, 0, fileBytes.Length);
stream.Close();
//Begins the process of writing the byte array back to a file
using (Stream file = File.OpenWrite(#"c:\path\to\your\file\here.txt"))
{
file.Write(fileBytes, 0, fileBytes.Length);
}
It may helps you...

#MultipartForm How to get the original file name?

I am using jboss's rest-easy multipart provider for importing a file. I read here http://docs.jboss.org/resteasy/docs/1.0.0.GA/userguide/html/Content_Marshalling_Providers.html#multipartform_annotation regarding #MultipartForm because I can exactly map it with my POJO.
Below is my POJO
public class SoftwarePackageForm {
#FormParam("softwarePackage")
private File file;
private String contentDisposition;
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getContentDisposition() {
return contentDisposition;
}
public void setContentDisposition(String contentDisposition) {
this.contentDisposition = contentDisposition;
}
}
Then I got the file object and printed its absolute path and it returned a file name of type file. The extension and uploaded file name are lost. My client is trying to upload a archive file(zip,tar,z)
I need this information at the server side so that I can apply the un-archive program properly.
The original file name is sent to the server in content-disposition header.
How can I get this information? Or atleast how can I say jboss to save the file with the uploaded file name and extension? Is it configurable from my application?
After looking around a bit for Resteasy examples including this one, it seems like there is no way to retrieve the original filename and extension information when using a POJO class with the #MultipartForm annotation.
The examples I have seen so far retrieve the filename from the Content-Disposition header from the "file" part of the submitted multiparts form data via HTTP POST, which essentially, looks something like:
Content-Disposition: form-data; name="file"; filename="your_file.zip"
Content-Type: application/zip
You will have to update your file upload REST service class to extract this header like this:
#POST
#Path("/upload")
#Consumes("multipart/form-data")
public Response uploadFile(MultipartFormDataInput input) {
String fileName = "";
Map<String, List<InputPart>> formParts = input.getFormDataMap();
List<InputPart> inPart = formParts.get("file"); // "file" should match the name attribute of your HTML file input
for (InputPart inputPart : inPart) {
try {
// Retrieve headers, read the Content-Disposition header to obtain the original name of the file
MultivaluedMap<String, String> headers = inputPart.getHeaders();
String[] contentDispositionHeader = headers.getFirst("Content-Disposition").split(";");
for (String name : contentDispositionHeader) {
if ((name.trim().startsWith("filename"))) {
String[] tmp = name.split("=");
fileName = tmp[1].trim().replaceAll("\"","");
}
}
// Handle the body of that part with an InputStream
InputStream istream = inputPart.getBody(InputStream.class,null);
/* ..etc.. */
}
catch (IOException e) {
e.printStackTrace();
}
}
String msgOutput = "Successfully uploaded file " + fileName;
return Response.status(200).entity(msgOutput).build();
}
Hope this helps.
You could use #PartFilename but unfortunately this is currently only used for writing forms, not reading forms: RESTEASY-1069.
Till this issue is fixed you could use MultipartFormDataInput as parameter for your resource method.
If you user MultipartFile class, than you can do something like:
MultipartFile multipartFile;
multipartFile.getOriginalFilename();
It seems that Isim is right, but there is a workaround.
Create a hidden field in your form and update its value with the selected file's name. When the form is submitted, the filename will be submitted as a #FormParam.
Here is some code you could need (jquery required).
<input id="the-file" type="file" name="file">
<input id="the-filename" type="hidden" name="filename">
<script>
$('#the-file').on('change', function(e) {
var filename = $(this).val();
var lastIndex = filename.lastIndexOf('\\');
if (lastIndex < 0) {
lastIndex = filename.lastIndexOf('/');
}
if (lastIndex >= 0) {
filename = filename.substring(lastIndex + 1);
}
$('#the-filename').val(filename);
});
</script>

Sending Multipart form data from windows phone to web api

Hi I want to send(post/put) some data(containing string, int and Stream) from windows phone 8.1 using HttpClient to web api. what is the best way to do that.
public async void Put(string uri)
{
var httpClient = new System.Net.Http.HttpClient();
MultipartFormDataContent content = new MultipartFormDataContent();
var stringContent = new StringContent("FirstName=MUH&LastName=Test", Encoding.UTF8, "multipart/form-data");
var test = new StreamContent(new MemoryStream());
content.Add(test);
content.Add(stringContent);
var message = await httpClient.PutAsync(url+"/UpdateTest", content);
message.EnsureSuccessStatusCode();
string content1 = await message.Content.ReadAsStringAsync();
}
api method in my mvc app
[AllowAnonymous]
[Route("~/api/account/UpdateTest")]
[HttpPut]
public async Task<object> UpdateTest()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
try
{
var requestParts = await Request.Content.ReadAsMultipartAsync();
foreach (var part in requestParts.Contents)
{
//part is always StreamContent
var test = await part.ReadAsStreamAsync();
var test1 = await part.ReadAsStringAsync();
}
}
catch (Exception ex)
{ }
}
In my windows phone project I have passed 2 HttpContent, one is StreamContent where as other is StringContent. but in my web api put method both are StreamContent I do't know why.
and other problem is I have to parse the string key value in StingContnet. My question is what is the best way of sending/receiving multipart form data from windows phone 8.1 to web api,
Thanks
Following is an example(change this accordingly to your scenario):
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseAddress);
HttpRequestMessage request = new HttpRequestMessage();
MultipartFormDataContent mfdc = new MultipartFormDataContent();
mfdc.Add(new StringContent("Michael"), name: "FirstName");
mfdc.Add(new StringContent("Jordan"), name: "LastName");
mfdc.Add(new StreamContent(content: new MemoryStream(Encoding.UTF8.GetBytes("This is from a file"))),
name: "Data",
fileName: "File1.txt");
HttpResponseMessage response = await client.PostAsync(baseAddress + "/api/values", mfdc);
public async Task Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
MultipartFormDataStreamProvider prov = await Request.Content.ReadAsMultipartAsync<MultipartFormDataStreamProvider>(new MultipartFormDataStreamProvider(#"C:\uploadedfiles"));
// example of how you can read the form data
string firstName = prov.FormData["FirstName"];
// Get list of all files that have been uploaded and stored in the above provided root folder
Collection<MultipartFileData> files = prov.FileData;
}
Following is how request looks like in Fiddler tool:
POST http://localhost:9095/api/values HTTP/1.1
Content-Type: multipart/form-data; boundary="7560a854-a71a-4e55-9571-5c2de520f45f"
Host: kirandesktop:9095
Content-Length: 474
Expect: 100-continue
Connection: Keep-Alive
--7560a854-a71a-4e55-9571-5c2de520f45f
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=FirstName
Michael
--7560a854-a71a-4e55-9571-5c2de520f45f
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=LastName
Jordan
--7560a854-a71a-4e55-9571-5c2de520f45f
Content-Disposition: form-data; name=Data; filename=File1.txt; filename*=utf-8''File1.txt
This is from a file
--7560a854-a71a-4e55-9571-5c2de520f45f--
Also note that you can read the StreamContent anyway you want...in the following examples, I am simulating a request's body stream and reading it as a simple string or deserializing into an object of type Person.
StreamContent requestStream = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("Hello World!")));
string data = await requestStream.ReadAsStringAsync();
//---------------------
StreamContent requestStream = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("{ \"FirstName\" : \"Michael\" }")));
requestStream.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Person person = await requestStream.ReadAsAsync<Person>();
I use a custom media type formatter that's based on the code in this article.
ASP.NET WebApi: MultipartDataMediaFormatter
One of the advantages over the MultipartFormDataStreamProvider is that I don't need to specify a folder to save the file data, so I can inspect the contents in memory. You might not want to do this with huge files though. There's alot a github repo for it too if you want to look at the code. Also, I get strongly typed objects for file and form data

File Upload with Additonal Form Data to Web Api from MVC

I am trying to upload a file with additional form data and post to Web API via MVC but i couldn't accomplish.
MVC Side
Firstly i got the submitted form at MVC. Here is the action for this.
[HttpPost]
public async Task<ActionResult> Edit(BrandInfo entity) {
try {
byte[] logoData = null;
if(Request.Files.Count > 0) {
HttpPostedFileBase logo = Request.Files[0];
logoData = new byte[logo.ContentLength];
logo.InputStream.Read(logoData, 0, logo.ContentLength);
entity.Logo = logo.FileName;
entity = await _repo.Update(entity.BrandID, entity, logoData);
}
else
entity = await _repo.Update(entity,entity.BrandID);
return RedirectToAction("Index", "Brand");
}
catch(HttpApiRequestException e) {
// logging, etc
return RedirectToAction("Index", "Brand");
}
}
Below code post the Multipartform to Web API
string requestUri = UriUtil.BuildRequestUri(_baseUri, uriTemplate, uriParameters: uriParameters);
MultipartFormDataContent formData = new MultipartFormDataContent();
StreamContent streamContent = null;
streamContent = new StreamContent(new MemoryStream(byteData));
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {
FileName = "\"" + fileName + "\"",
Name = "\"filename\""
};
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
formData.Add(streamContent);
formData.Add(new ObjectContent<TRequestModel>(requestModel, _writerMediaTypeFormatter), "entity");
return _httpClient.PutAsync(requestUri, formData).GetHttpApiResponseAsync<TResult>(_formatters);
As you can see i am trying to send file data and object with same MultipartFormDataContent. I couldn't find better way to send my entity as ObjectContent. Also i am using JSON.Net Serializer
Regarding to fiddler, post seems successfull.
PUT http://localhost:12836/api/brand/updatewithlogo/13 HTTP/1.1
Content-Type: multipart/form-data; boundary="10255239-d2a3-449d-8fad-2f31b1d00d2a"
Host: localhost:12836
Content-Length: 4341
Expect: 100-continue
--10255239-d2a3-449d-8fad-2f31b1d00d2a
Content-Disposition: form-data; filename="web-host-logo.gif"; name="filename"
Content-Type: application/octet-stream
GIF89a��L������X�������wW����������xH�U�)�-�k6�������v6�������̥�v�J���������7����V:�=#�ի�I(�xf�$�������
// byte data
// byte data
'pf�Y��y�ؙ�ڹ�(�;
--10255239-d2a3-449d-8fad-2f31b1d00d2a
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name=entity
{"BrandID":13,"AssetType":null,"AssetTypeID":2,"Logo":"web-host-logo.gif","Name":"Geçici Brand","Models":null,"SupplierBrands":null}
--10255239-d2a3-449d-8fad-2f31b1d00d2a--
Web API Side
Finally i am catching post at Web API side and trying to parse but i couldn't. Because MultipartFormDataStreamProvider's FileData and FormData collections are allways empty.
[HttpPut]
public void UpdateWithLogo(int id) {
if(Request.Content.IsMimeMultipartContent()) {
var x = 1; // this code has no sense, only here to check IsMimeMultipartContent
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try {
// Read the form data.
Request.Content.ReadAsMultipartAsync(provider);
foreach(var key in provider.FormData.AllKeys) {
foreach(var val in provider.FormData.GetValues(key)) {
_logger.Info(string.Format("{0}: {1}", key, val));
}
}
// This illustrates how to get the file names.
foreach(MultipartFileData file in provider.FileData) {
_logger.Info(file.Headers.ContentDisposition.FileName);
_logger.Info("Server file path: " + file.LocalFileName);
}
}
catch(Exception e) {
throw new HttpApiRequestException("Error", HttpStatusCode.InternalServerError, null);
}
}
I hope you can help to find my mistake.
UPDATE
I also realized that, if i comment out StreamContent or ObjectContent and only add StringContent, still i can't get anything from MultipartFormDataStreamProvider.
Finally i resolved my problem and it was all about async :)
As you can see at API action method i had called ReadAsMultipartAsync method synchrously but this was a mistake. I had to call it with ContinueWith so after i changed my code like below my problem solved.
var files = Request.Content.ReadAsMultipartAsync(provider).ContinueWith<HttpResponseMessage>(task => {
if(task.IsFaulted)
throw task.Exception;
// do additional stuff
});