Using the Microsoft Graph v1.0 api and C#, I am able to update (patch) existing OneNote pages however after writing image data to the multipart/form-data section, the resulting image height and width are correct, but, the image is not rendered on the page -- other than an empty image place-holder.
So the question is, what is the correct image format expected by OneNote for a PATCH command? The docs state that it must be 'binary image data'. Shouldn't File.ReadAllBytes be sufficient?
Here are the formats attempted so far:
string file = #"c:\images\file1.jpg";
var rawData = System.IO.File.ReadAllText(file); //attempt1
byte[] bytes = System.IO.File.ReadAllBytes(file); //attempt2
var byteArray = BitConverter.ToString(bytes); //attempt3
var byteString = Encoding.ASCII.GetString(bytes); //attempt4
string base64String = Convert.ToBase64String(bytes, 0, bytes.Length); //attempt5
string imageDataURL = string.Format("data:image/jpeg;base64,{0}", base64String); //attempt6
...
/* Construct message content */
StringBuilder sb = new StringBuilder();
sb.Append("--MyPartBoundary198374" + "\r\n");
sb.Append(#"Content-Disposition: form-data; name=""Commands""" + "\r\n");
sb.Append("Content-Type: application/json" + "\r\n" + "\r\n");
sb.Append(
#"[{
'target':'body',
'action':'append',
'position':'before',
'content':'<img src=""name:image1"" width=""400"" height=""500""/>'
}]"
+ "\r\n" + "\r\n");
sb.Append("--MyPartBoundary198374" + "\r\n");
sb.Append(#"Content-Disposition: form-data; name=""image1""" + "\r\n");
sb.Append("Content-Type: image/jpeg" + "\r\n\r\n" );
sb.Append([see formats above] + "\r\n");
sb.Append("--MyPartBoundary198374--" );
string content = sb.ToString();
string contentType = "multipart/form-data; boundary=MyPartBoundary198374";
var result = await OneNoteService.UpdatePageAsync(client, page, contentType, content);
...
internal static async Task <HttpResponseMessage> UpdatePageAsync(GraphServiceClient client, OnenotePage page, string contentType, string content)
{
HttpResponseMessage response = null;
try
{
string requestUri = client.Users[ME].Onenote.Pages[page.Id].Content.Request().RequestUrl;
List<OnenotePatchContentCommand> patchCommands = new List<OnenotePatchContentCommand>();
HttpRequestMessage request = new HttpRequestMessage()
{
Method = new HttpMethod("PATCH"),
RequestUri = new Uri(requestUri),
Content = new StringContent(content, Encoding.UTF8, "application/json"),
};
request.Content.Headers.Remove("Content-Type");
request.Content.Headers.TryAddWithoutValidation("Content-Type", contentType);
// Adds the user's access token from the GraphServiceClient to the request.
await client.AuthenticationProvider.AuthenticateRequestAsync(request);
response = await client.HttpProvider.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
throw new Microsoft.Graph.ServiceException(
new Error
{
Code = response.StatusCode.ToString(),
Message = await response.Content.ReadAsStringAsync()
});
}
}
catch (Exception ex)
{
//TODO: Error handling
Console.WriteLine(ex.ToString());
}
return response;
}
The suggestions listed in this post did not resolve the issue:
Inserting image into existing OneNote page via REST api not working
Been trying to resolve this for more than a day. Can someone provide the proper image data format expected by the PATCH command.
Thanks,
Roland
Moving previous comment to an answer in case others run into this.
Rather than sending multiple parts, combine attempts 5 & 6 and include the result directly in the src attribute:
string base64String = Convert.ToBase64String(bytes, 0, bytes.Length); //attempt5
string imageDataURL = string.Format("data:image/jpeg;base64,{0}", base64String); //attempt6
/* Construct message content */
StringBuilder sb = new StringBuilder();
sb.Append("--MyPartBoundary198374" + "\r\n");
sb.Append(#"Content-Disposition: form-data; name=""Commands""" + "\r\n");
sb.Append("Content-Type: application/json" + "\r\n" + "\r\n");
sb.Append(
#"[{
'target':'body',
'action':'append',
'position':'before',
'content':'<img src=" + imageDataURL + " width=""400"" height=""500""/>'
}]"
+ "\r\n" + "\r\n");
This will include the image directly in the HTML you're inserting.
Related
string boundray = "---------------------------" + DateTime.Now.Ticks.ToString("x");
string url = HttpDomainHandling(currentUser.DomainURL) + API + "&is_multi_part_upload=true";
string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\n" + "Content-Type: {3}\r\n\r\n", boundray, "file", file.Name, "application/octet-stream");
string footer = string.Format("\r\n--{0}--\r\n", boundray);
Stream headerStream = GenerateStreamFromString(header);
Stream footerStream = GenerateStreamFromString(footer);
Stream dataStream = await sfile.OpenStreamForReadAsync();
MemoryStream fileDataStream = new MemoryStream();
await headerStream.CopyToAsync(fileDataStream);
await dataStream.CopyToAsync(fileDataStream);
await footerStream.CopyToAsync(fileDataStream);
fileDataStream.Position = 0;
IInputStream stream = fileDataStream.AsInputStream();
BackgroundUploader backgroundUploader = new BackgroundUploader();
backgroundUploader.SetRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundray);
backgroundUploader.SetRequestHeader("Cookie", Constants.FELIXSESSIONID + "=" + currentUser.SessionID);
backgroundUploader.Method = "POST";
UploadOperation uploadOpration = await backgroundUploader.CreateUploadFromStreamAsync(new Uri(url), stream);
await Task.Factory.StartNew(() => CheckUploadStatus(uploadOpration, progressEvent, cts));
var result = await uploadOpration.StartAsync();
ResponseInformation info = uploadOpration.GetResponseInformation();
return info;
Unable to find json response in result and response information..where can i get this response...
I am trying to upload file to my server.. and its return upload data in json format..
Finally got the Answer
BackgroundUploader is used when you want to upload something on IP or some address and you don't want the response string from server...
BackgroundUploader can return just status code and successfully or error message info
if you want to upload something and you want you response string example JSON or XML...
need to use UWP HTTPCLIENT
//Create an HTTP client object
Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
//Add a user-agent header to the GET request.
var headers = httpClient.DefaultRequestHeaders;
//The safe way to add a header value is to use the TryParseAdd method and verify the return value is true,
//especially if the header value is coming from user input.
string header = "ie";
if (!headers.UserAgent.TryParseAdd(header))
{
throw new Exception("Invalid header value: " + header);
}
header = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)";
if (!headers.UserAgent.TryParseAdd(header))
{
throw new Exception("Invalid header value: " + header);
}
Uri requestUri = new Uri("http://www.contoso.com");
//Send the GET request asynchronously and retrieve the response as a string.
Windows.Web.Http.HttpResponseMessage httpResponse = new Windows.Web.Http.HttpResponseMessage();
string httpResponseBody = "";
try
{
//Send the GET request
httpResponse = await httpClient.GetAsync(requestUri);
httpResponse.EnsureSuccessStatusCode();
httpResponseBody = await httpResponse.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
httpResponseBody = "Error: " + ex.HResult.ToString("X") + " Message: " + ex.Message;
}
I'm not able to read the file name as well as other values that come from the client. I'm using HttpWebRequest to send Multi part data to the server. My client side code looks like so:
public string upload(string file, string url)
{
HttpWebRequest requestToServer = (HttpWebRequest)
WebRequest.Create(url);
// Define a boundary string
string boundaryString = "----";
// Turn off the buffering of data to be written, to prevent
// OutOfMemoryException when sending data
requestToServer.AllowWriteStreamBuffering = false;
// Specify that request is a HTTP post
requestToServer.Method = WebRequestMethods.Http.Post;
// Specify that the content type is a multipart request
requestToServer.ContentType
= "multipart/form-data; boundary=" + boundaryString;
// Turn off keep alive
requestToServer.KeepAlive = false;
ASCIIEncoding ascii = new ASCIIEncoding();
string boundaryStringLine = "\r\n--" + boundaryString + "\r\n";
byte[] boundaryStringLineBytes = ascii.GetBytes(boundaryStringLine);
string lastBoundaryStringLine = "\r\n--" + boundaryString + "--\r\n";
byte[] lastBoundaryStringLineBytes = ascii.GetBytes(lastBoundaryStringLine);
NameValueCollection nvc = new NameValueCollection();
nvc.Add("id", "TTR");
// Get the byte array of the myFileDescription content disposition
string myFileDescriptionContentDisposition = Java.Lang.String.Format(
"Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}",
"myFileDescription",
"A sample file description");
byte[] myFileDescriptionContentDispositionBytes
= ascii.GetBytes(myFileDescriptionContentDisposition);
string fileUrl = file;
// Get the byte array of the string part of the myFile content
// disposition
string myFileContentDisposition = Java.Lang.String.Format(
"Content-Disposition: form-data;name=\"{0}\"; "
+ "filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n",
"myFile", Path.GetFileName(fileUrl), Path.GetExtension(fileUrl));
byte[] myFileContentDispositionBytes =
ascii.GetBytes(myFileContentDisposition);
var name = Path.GetFileName(fileUrl);
FileInfo fileInfo = new FileInfo(fileUrl);
// Calculate the total size of the HTTP request
long totalRequestBodySize = boundaryStringLineBytes.Length * 2
+ lastBoundaryStringLineBytes.Length
+ myFileDescriptionContentDispositionBytes.Length
+ myFileContentDispositionBytes.Length
+ fileInfo.Length;
// And indicate the value as the HTTP request content length
requestToServer.ContentLength = totalRequestBodySize;
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
// Write the http request body directly to the server
using (Stream s = requestToServer.GetRequestStream())
{
//foreach (string key in nvc.Keys)
//{
// s.Write(boundaryStringLineBytes, 0, boundaryStringLineBytes.Length);
// string formitem = string.Format(formdataTemplate, key, nvc[key]);
// byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
// s.Write(formitembytes, 0, formitembytes.Length);
//}
// Send the file description content disposition over to the server
s.Write(boundaryStringLineBytes, 0, boundaryStringLineBytes.Length);
s.Write(myFileDescriptionContentDispositionBytes, 0,
myFileDescriptionContentDispositionBytes.Length);
// Send the file content disposition over to the server
s.Write(boundaryStringLineBytes, 0, boundaryStringLineBytes.Length);
s.Write(myFileContentDispositionBytes, 0,
myFileContentDispositionBytes.Length);
// Send the file binaries over to the server, in 1024 bytes chunk
FileStream fileStream = new FileStream(fileUrl, FileMode.Open,
FileAccess.Read);
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
s.Write(buffer, 0, bytesRead);
} // end while
fileStream.Close();
// Send the last part of the HTTP request body
s.Write(lastBoundaryStringLineBytes, 0, lastBoundaryStringLineBytes.Length);
WebResponse response = requestToServer.GetResponse();
StreamReader responseReader = new StreamReader(response.GetResponseStream());
string replyFromServer = responseReader.ReadToEnd();
return replyFromServer;
}
}
The content-disposition values that are written on the client side aren't being retrieved on the server side. On the server side, the file name is read as "{0}" and subsequently other values are read as "{1}" and "{2}".
My server side code looks like so:
public async Task<HttpResponseMessage> UploadFile()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
var httpRequest = HttpContext.Current.Request;
var id = httpRequest.Form["{0}"];
var id2 = httpRequest.Form[0];
var s = id;
var l = id2;
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
postedFile.SaveAs(filePath);
// NOTE: To store in memory use postedFile.InputStream
}
return Request.CreateResponse(HttpStatusCode.Created);
}
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
I've been stuck on this for 2 days and it's driving me crazy. I have chopped and changed my code several time but each time I have different issues. This is the closest I have come to making my code work except reading the headers properly on the server.
I will forever be grateful to the person the person who will help me out.
Your client is most likely something like Android application. You use Java.Lang.String.Format there, and syntax of java format strings is different from .NET format strings, so your {0} {1} etc placesholders are not getting expanded. To fix, just use regular .NET String.Format.
I wrote a program to post tasks to asana through the API and it has been working fine up until this morning, can anyone help me figure out why that is?
this is an example of the JSON string I am sending:
{"workspace":09876543321111,"data": {"assignee":null,"name":"Sample Name","notes":"Sample Noted","due_on":"2015-01-27","projects":"12434567889099","completed":false}}
and I am getting a 400 error: bad request.
this is my code:
string ID = "09876543321111"; //workspace ID
string url = #"https://app.asana.com/api/1.0/workspaces/" + ID + #"/tasks";
Data dat = new Data();
string ProjName = "Test Project";
dat.projects = "1234567890234";
dat.assignee = null;
dat.name = "Sample Name";
dat.notes = "Sample Notes";
dat.due_on = val.requiredBy.Value.ToString("u").Substring(0, 10);
dat.completed = false;
//if task doesnt exist, make one
if (!Tasks.CheckExist(project, dat.projects, dat.name, apiKey, log))
{
string json = JsonConvert.SerializeObject(dat);
string data = "{\"workspace\":" + ID + ",\"data\": " + json + "}";
log.WriteLine(data);
Functions.Post(data, url, apiKey, log);
}
Post function:
//post tasks to asana
public static void Post(string data, string url, string apiKey, StreamWriter log)
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
var req = (HttpWebRequest)WebRequest.Create(url);
req.Method = WebRequestMethods.Http.Post;
req.ContentLength = bytes.Length;
req.ContentType = "application/json";
var authInfo = apiKey + ":";
var encodedAuthInfo = Convert.ToBase64String(
Encoding.Default.GetBytes(authInfo));
req.Headers.Add("Authorization: Basic " + encodedAuthInfo);
req.ContentLength = bytes.Length;
Stream reqStream = req.GetRequestStream();
reqStream.Write(bytes, 0, bytes.Length);
reqStream.Close();
try
{
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
string res = new StreamReader(response.GetResponseStream()).ReadToEnd();
}
catch (WebException ex)
{
HttpWebResponse response = ((HttpWebResponse)ex.Response);
string exc = url + " caused a " + (int)response.StatusCode + " error.\n" + response.StatusDescription;
Console.WriteLine(exc);
log.WriteLine(exc);
}
}
EDIT
for anyone who cares I solved the problem by changing string data to:
string data = "{\"data\": " + json + "}";
We recently made a change to return a 400 error if there were unexpected parameters passed at the top level, as (nearly) all API routes only use the parameters passed in under the "data" attribute. In this case (as you correctly determined) the "workspace" attribute at the top level was incorrect - previously we just ignored it, but in an effort to make the API less "surprising" we wanted to be explicit and strict about parameters that could be ignored, as otherwise it could be misleading.
I have a c# function that is used to upload a file to a PHP web service. The PHP web service is expecting the following
A POST parameter called UploadFileRequestDto containing some XML data
The File stream
For some odd reason the $_POST parameter contains the UploadFileRequestDto only some of the time. If I look at the contents of
file_get_contents("php://input"))
I can see that the request is comming through as expected with the UploadFileRequestDto included.
Doing a
print_r($_REQUEST)
is returning an empty array.
Can anyone help me with a solution to this problem, my C# function is stipulated below
public string UploadFile(UploadFileRequestDto uploadFileRequestDto,string fileToUpload, string fileUploadEndpoint)
{
try
{
var request = (HttpWebRequest)WebRequest.Create(fileUploadEndpoint);
request.ReadWriteTimeout = 1000 * 60 * 10;
request.Timeout = 1000 * 60 * 10;
request.KeepAlive = false;
var boundary = "B0unD-Ary";
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.Method = "POST";
var postData = "--" + boundary + "\r\nContent-Disposition: form-data;";
postData += "name=\"UploadFileRequestDto\"\r\n\r\n";
postData += string.Format("{0}\r\n", SerializeUploadfileRequestDto(uploadFileRequestDto));
postData += "--" + boundary + "\r\n";
postData += "--" + boundary + "\r\nContent-Disposition: form-data;name=\"file\";filename=\"" + Path.GetFileName(fileToUpload) + "\"\r\n";
postData += "Content-Type: multipart/form-data\r\n\r\n";
var byteArray = Encoding.UTF8.GetBytes(postData);
byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
byte[] filedata = null;
using (var reader = new BinaryReader(File.OpenRead(fileToUpload)))
{
filedata = reader.ReadBytes((int)reader.BaseStream.Length);
}
request.ContentLength = byteArray.Length + filedata.Length + boundaryBytes.Length;
request.GetRequestStream().Write(byteArray, 0, byteArray.Length);
request.GetRequestStream().Write(filedata, 0, filedata.Length);
request.GetRequestStream().Write(boundaryBytes, 0, boundaryBytes.Length);
var response = request.GetResponse();
var data = response.GetResponseStream();
var sReader = new StreamReader(data);
var sResponse = sReader.ReadToEnd();
response.Close();
return sResponse.TrimStart(new char[] { '\r', '\n' });
}
catch (Exception ex)
{
LogProvider.Error(string.Format("OzLib.Infrastructure : WebHelper : public string UploadFile(UploadFileRequestDto uploadFileRequestDto, string fileUploadEndpoint) : Exception = {0}", ex.ToString()));
}
Ok I found the problem, the
post_max_size
setting in the php.ini was set to 8M and some of the files I was trying to upload exeeded 8M. Changed this setting to 16M and restarted the PHP service.
When the file size exeeds the limit that was set the $_POST global is empty.
I am trying to use the ImageShack API to upload images. To use it, I am supposed to POST the image using multipart/form-data. I did it like ...
var postData = "";
var req = HttpWebRequest.Create("http://www.imageshack.us/upload_api.php");
req.Method = "POST";
req.ContentType = "multipart/form-data";
postData += "key=my_key_here&";
postData += "type=base64&";
// get base64 data from image
byte[] bytes = File.ReadAllBytes(#"D:\tmp\WpfApplication1\WpfApplication1\Images\Icon128.gif");
string encoded = Convert.ToBase64String(bytes);
postData += "fileupload=" + encoded;
byte[] reqData = Encoding.UTF8.GetBytes(postData);
using (Stream dataStream = req.GetRequestStream())
{
dataStream.Write(reqData, 0, reqData.Length);
}
var res = (HttpWebResponse)req.GetResponse();
var resStream = res.GetResponseStream();
var reader = new StreamReader(resStream);
string resString = reader.ReadToEnd();
txt1.Text = resString;
but ImageShack is complaining that
<links>
<error id="parameter_missing">Sorry, but we've detected that unexpected data is received. Required parameter 'fileupload' is missing or your post is not multipart/form-data</error>
</links>
FileUpload is present and I am using multipart/form-data whats wrong?
UPDATE:
New Code http://pastebin.com/TN6e0CD8
Post data http://pastebin.com/fYE9fsxs
UPDATE 2
i looked at the other question Multipart forms from C# client. modified my code with boundary, removed the expect 100 header still i cant get it working ...
ServicePointManager.Expect100Continue = false;
var boundary = "-----------------------------28520690214962";
var newLine = Environment.NewLine;
var propFormat = boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine + newLine;
var fileHeaderFormat = boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + newLine;
var req = (HttpWebRequest)HttpWebRequest.Create("http://jm/php/upload.php");
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data; boundary=" + boundary;
using (var reqStream = req.GetRequestStream()) {
var reqWriter = new StreamWriter(reqStream);
var tmp = string.Format(propFormat, "str1", "hello world");
reqWriter.Write(tmp);
tmp = string.Format(propFormat, "str2", "hello world 2");
reqWriter.Write(tmp);
reqWriter.Write(boundary + "--");
reqWriter.Flush();
}
var res = req.GetResponse();
using (var resStream = res.GetResponseStream()) {
var reader = new StreamReader(resStream);
txt1.Text = reader.ReadToEnd();
}
I finally got it with the following code ...
var boundary = "------------------------" + DateTime.Now.Ticks;
var newLine = Environment.NewLine;
var propFormat = "--" + boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine;
var fileHeaderFormat = "--" + boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + newLine;
var req = (HttpWebRequest)HttpWebRequest.Create("http://jm/php/upload.php");
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data; boundary=" + boundary;
using (var reqStream = req.GetRequestStream()) {
var reqWriter = new StreamWriter(reqStream);
var tmp = string.Format(propFormat, "str1", "hello world");
reqWriter.Write(tmp);
tmp = string.Format(propFormat, "str2", "hello world 2");
reqWriter.Write(tmp);
reqWriter.Write("--" + boundary + "--");
reqWriter.Flush();
}
var res = req.GetResponse();
using (var resStream = res.GetResponseStream()) {
var reader = new StreamReader(resStream);
txt1.Text = reader.ReadToEnd();
}
Notice boundaries have to begin with -- {boundary declared in ContentType} and ending boundary must begin & end with -- . in my case, I originally used
var propFormat = boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine;
replace it with
var propFormat = "--" + boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine;
and everything works
I believe that you are not building the request body correctly.
First, you need to include part boundary (random text) in content type header. For example,
Content-Type: multipart/form-data;
boundary=----WebKitFormBoundarySkAQdHysJKel8YBM
Now format of request body will be something like
------WebKitFormBoundarySkAQdHysJKel8YBM
Content-Disposition: form-data;name="key"
KeyValueGoesHere
------WebKitFormBoundarySkAQdHysJKel8YBM
Content-Disposition: form-data;name="param2"
ValueHere
------WebKitFormBoundarySkAQdHysJKel8YBM
Content-Disposition: form-data;name="fileUpload"; filename="y1.jpg"
Content-Type: image/jpeg
[image data goes here]
I will suggest you to use tool such as Fiddler to understand how these requests are built.
This is nothing like multipart/form-data
There's no boundaries between fields (needed even with one field).
Why are you base-64 encoding?
There's no indication of the content-type of the image.
Take a look at RFC 2388 for the actual format spec. It can also be useful to look at a Fiddler grab of a file upload from a web-page.