I have recieved an image with some text while reading from a NetworkStream. The stream includes something like this:
HTTP/1.0 200 OK
Expires: -1
Cache-Control: no-cache
Content-length: 29160
Content-type: image/jpeg
...followed by the image.
How can I read just the image from the NetworkStream?
You would have to parse the HTTP header first, to know where to stop discarding data. Alternatively, save the whole thing and then examine it afterwards, which may be simpler. Basically you'd be looking for two ASCII carriage-return/line-feed ("\r\n") pairs in a row.
However, there's a much better alternative: use an HTTP library. Parsing this yourself is like using text manipulation to handle XML; you're better off working at a higher level of abstraction with code which has been well tested for that abstraction.
Related
I would like to send a file through an API. The server will receive the file and save it on the server drive.
The question is that I have two Options to do this:
Read the file as a string and send the whole string in the request Body
Use multi-part
Could someone help in the pros and cons in using the two options.
It's not related to pros and cons, So basically to understand what is multipart means
Multipart requests combine one or more sets of data into a single
body, separated by boundaries. You typically use these requests for
file uploads and for transferring data of several types in a single
request (for example, a file along with a JSON object).
This mean that multipart contains different section of info that separated by boundary(random number).
For example:
POST /upload HTTP/1.1
Content-Length: 428
Content-Type: multipart/form-data; boundary=abcde12345
--abcde12345
Content-Disposition: form-data; name="id"
Content-Type: text/plain
{...Additional plain content goes here...}
--abcde12345
Content-Disposition: form-data; name="address"
Content-Type: application/json
{
"street": "3, Garden St",
"city": "Hillsbery, UT"
}
--abcde12345
Content-Disposition: form-data; name="profileImage "; filename="image1.png"
Content-Type: application/octet-stream
{...file content...}
--abcde12345--
In this example you can see different content sent in one request separated by boundary [boundary=abcde12345]. (Plain text, Json Object and File Content)
The file content section here is used for sending file data in application/octet-stream (string in the binary or base64 format), so basically you are sending the file data in form of string :) as you referred to the first point, but you can include some additional info, may be the new file name to be saved , the user who uploaded this file or whatever you want.
Hope you get the point.
Ref:
https://swagger.io/docs/specification/describing-request-body/file-upload/
https://swagger.io/docs/specification/describing-request-body/multipart-requests/
The only difference on the protocol level is that multipart/form-data requests must adhere to RFC 2388 while a custom typed request body can be arbitrary.
Using Base64 string has an advantage for uploading very tiny individual images. It's easy to handle and avoids dependency on Http.IFormFile. On the contrary, base64 encoded files are larger than the original and you need decode it on the server side.
I want to upload a file in WindChill (is a PLM from PTC). They give to us an REST API with the services to do this. They split an upload of file in 3 stages.
Stage 1 - We call a service where we give the number of files to upload. In this case only one.
Stage 2 - A multipart/formdata where we give the file to upload.
Stage 3 - The last stage where we give the file name, the file size etc...
I think my problem is on stage 2.
All the stages run successfully but when i try to open the uploaded file, in this case a pdf, the file is blank, but with the same number of pages of the original one. I compare the content of the uploaded file with the original one and the content inside is the same with a big difference. The original is with an ANSI encoding while the uploaded one is with the UTF-8 encoding. So, I think my problem is on the stage 2.
I'm with some doubts on this stage. In C# I get the bytes[] of file, but in the end I need to pass this bytes to a string to send in a multipart form. What is the encoding that i should use to get string? I tested with default, UTF-8, UNICODE, ASCII encoding but nothing.
Here is the example of the Post request body. In a C# I use the HTTPWebRequest to make a request.
------boundary
Content-Disposition: form-data; name="Master_URL"
https://MyUrl/Windchill/servlet/WindchillGW
------boundary
Content-Disposition: form-data; name="CacheDescriptor_array"
844032:844032:844032;
------boundary
Content-Disposition: form-data; name="844032"; filename="newDoc.pdf"
Content-Type: application/pdf
%PDF-1.7 //// The content of the file starts here
%ยตยตยตยต
1 0 obj
........
------boundary--
Before this approach I tried to convert the bytes[] ToBase64String and send an body like this:
------boundary
Content-Disposition: form-data; name="Master_URL"
https://MyUrl/Windchill/servlet/WindchillGW
------boundary
Content-Disposition: form-data; name="CacheDescriptor_array"
844033:844033:844033;
------boundary
Content-Disposition: form-data; name="844033"; filename="newDoc.pdf"
Content-Type: application/pdf
Content-Transfer-Encoding: base64
JVBERi0xLjcNCiW1tbW1DQox ........ //// The content of the file starts here
------boundary--
In this case, when I try to open the file i get the error "Failed to load PDF document". The file is corrupt.
I think the problem is on the stage 2, but I will share the body that i send in last stage for your understanding.
{"ContentInfo":[{"StreamId":844034,"EncodedInfo": "844034%3A40384%3A9276564%3A844034","FileName": "newDoc.pdf","PrimaryContent": true,"MimeType" : "application/pdf","FileSize" : 40384}]}
The StreamId and the EncodedInfo are returns of the stage 2 that I need to provide in the stage 3.
Anyone can see what I'm doing wrong? Anyone have some tips to help me to solve this issue?
Many thanks.
I have a big tip for solve issues like that.
Use postman. Make all the job in postman. After your job is done, you can generate the code for multiple languages with postman. Many thanks.
I'm trying to "repurpose" a third-party API used by a desktop application. I've found that the below code gets me very close to matching the packets sent by the app:
var formData = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>(JsonConvert.SerializeObject(myPayload), "")
});
var response = Client.PostAsync(myURL, formData).Result;
var json = response.Content.ReadAsStringAsync().Result;
This gets me almost exactly the same payload sent by the application, except it encodes the data (I know, "encoded" is right there in the name). I need to get the exact same request but without the data being encoded, but I can't quite find the right object(s) to pull it off. How do I keep this payload from being URL encoded?
Edit:
This is a login request I pulled from Wireshark emanating from the application:
POST /Login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: 1.1.1.1
Content-Length: 161
Expect: 100-continue
Connection: Close
{"username":"myuser","auth-id":"0a0a140f81a2ce0c303386e93cec41bf04660c22a881be9a"}
This is what the above will generate:
POST /Login HTTP/1.1
Expect: 100-continue
Connection: Close
Content-Type: application/x-www-form-urlencoded
Content-Length: 221
Host: 1.1.1.1
%7B%22user-name%22%3A%22myuser%22%2C%22auth-id%22%3A%220a0a140f81a2ce0c303386e93cec41bf04660c22a881be9a%22%7D=
I've edited them for brevity so the Content-Length is wrong. I realize it might not be the best way to send this data, but I have no control over how it's consumed.
Since you're actually trying to send JSON, I think you need to wrap the JSON in a StringContent object rather than a FormUrlEncoded object. Form-encoded data and JSON data are two different ways of formatting a payload (another commonly used format would be XML, for example). Using them both together doesn't make any sense.
I think something like this should work:
var content = new StringContent(JsonConvert.SerializeObject(myPayload), Encoding.UTF8, "application/json");
var response = Client.PostAsync(myURL, content).Result;
var json = response.Content.ReadAsStringAsync().Result;
(P.S. the Content-Type: application/x-www-form-urlencoded header sent by the application appears to be misleading, since the request body clearly contains JSON. Presumably the receiving server is tolerant of this nonsense, or just ignores it because it's always expecting JSON.)
I am a bit puzzled at the moment. I have a web application that manipulates a file and then returns the file to a user's browser for download when it's done.
The download part is going well, as I'm using Response.AddHeader and Reponse.BinaryWrite to push the file back to the browser but I am unable to call any further methods after using Response methods.
I suppose I have not worked with HttpReponse enough to know the trick to this. Perhaps I would be better off using another class or generic handler to handle the download?
My code goes something like...
// Methods to be called first
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}.pdf", "New_Merged_PDF_" + DateTime.Now.ToString("MMMM-dd-yyyy")));
Response.ContentType = "application/pdf";
Response.BinaryWrite(output.ToArray());
Response.End();
// Methods to be called last (these wont work)
Probably something simple that I'm overlooking but I'm still trying to figure it out.
To add a little color to Servy's explaination; there is an order of operations within the HTTP protocol specification. One of them is that the Headers need to be sent to the client before the Body. This allows the receiver of the response to properly deal with the Body that is sent based on any Headers.
The presence of a message-body in a request is signaled by the
inclusion of a Content-Length or Transfer-Encoding header field in
the request's message-headers.
IETF RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1
One of the few (if the only, I'm not sure) exception is the Content-Type: multipart/mixed;
IETF RFC 1867 - Form-based File Upload in HTML (although Obsolete, examples are still relevant)
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
I am trying to extract the headers of the body part of a multipart mail message in Outlook. The raw message (which I have not been able to get from my code) looks something like this:
Return-Path: ...
Received: ...
From: ...
Content-Type: multipart/signed; boundary="Apple-Mail=_06FDFEBB-366E-4B1E-AA7F-F5DDEC13FD03"; protocol="application/pgp-signature"; micalg=pgp-sha512
Subject: ...
Message-Id: ...
Date: ...
To: ...
Mime-Version: ...
X-Mailer: ...
--Apple-Mail=_06FDFEBB-366E-4B1E-AA7F-F5DDEC13FD03
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=us-ascii
...
--Apple-Mail=_06FDFEBB-366E-4B1E-AA7F-F5DDEC13FD03
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename=signature.asc
Content-Type: application/pgp-signature;
name=signature.asc
Content-Description: Message signed with OpenPGP using GPGMail
...
--Apple-Mail=_06FDFEBB-366E-4B1E-AA7F-F5DDEC13FD03--
I have replaced some of the none relevant parts by dots. The headers I am trying to get are the ones under the first boundary. So this is the part I am looking for:
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=us-ascii
However, if I could get the entire part between the boundaries, that would also be fine as I could parse it myself.
So far, I have only been able to get the headers at the top of the message (so from Return-Path until X-Mailer).
I was able to do that using a `PropertyAccessor in the following way:
mailItem.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")
In this case mailItem is my Microsoft.Office.Interop.Outlook.MailItem instance.
So, what my question comes down to: How can I get the headers under the first boundary, or any bigger part of the message that contains them?
For signed messages, Outlook preserves the signed message body (with full MIME data) in an attachment called smime.p7m (it's always called smime, even if it's actually PGP/MIME). Unfortunately, Outlook hides this from you, transparently unpacking the signed message and displaying it instead. There's no way, using the Outlook Object Model, to get the actual message body.
However, if you're willing to call MAPI directly (easiest from native code, but can be done from .NET if you're not afraid of some nasty COM interop), you can get the multipart/signed body - both the signature and the complete signed part - as follows:
Starting from the Outlook MailItem, get the MAPIOBJECT property. This is actually a MAPI IMessage. Cast it to an IMAPISecureMessage (.NET will handle this as a QueryInterface behind the scenes). Call GetBaseMessage() on this IMAPISecureMessage (the only documented function), which returns another IMessage. This is the "real" message, the one with the smime.p7m attachment. Unfortunately, there's no way to put this back into OOM, so you have to continue using MAPI. By calling the functions on IMessage, you can get the attachment, then get its data. You'll need to parse the MIME parts, at least enough to get the signed part without its headers, outer boundaries, or of course the signature part. Verify that signed part (without decoding its internal parts, if any, or decoding quoted-printable or anything like that) against the signature blob.
PR_TRANSPORT_MESSAGE_HEADERS property is the only thing you can get. Outlook does not store the full MIME source of the original message.
You can see what is available in OutlookSpy (I am its author) - click IMessage button.