In my application for Blackberry OS 6.0 I am posting multiple image files using HttpConnection,
here is what I am trying,
byte[] _dataToBePost = strPostData.getBytes();
String lineEnd = "\r\n";
String boundary = "----------------------------";
String boundaryStartBytes = "------------------------------\r\n";
byte[] startBytes = boundaryStartBytes.getBytes();
String boundaryEndBytes = "\r\n------------------------------\r\n";
byte[] endBytes = boundaryEndBytes.getBytes();
_httpConnection = (HttpConnection)Connector.open(url,Connector.READ_WRITE,true);
// Set the request method and headers
_httpConnection.setRequestMethod(HttpConnection.POST);
_httpConnection.setRequestProperty("If-Modified-Since","29 Oct 1999 19:43:31 GMT");
_httpConnection.setRequestProperty("User-Agent","Profile/MIDP-2.0 Configuration/CLDC-1.0");
_httpConnection.setRequestProperty("Content-Language", "en-US");
if( PhotoToSend != null)
//if(AttachPhotos._vctAccPhotos.size() > 0)
{
String[] strAccidentPhoto = {"AccidentPhoto1", "AccidentPhoto2", "AccidentPhoto3", "AccidentPhoto4", "AccidentPhoto5"};
for(int i=0; i<5; i++)
{
String header = "Content-Disposition: form-data; name=\"file1\";filename=\""+ "AccidentPhoto"+ i +".jpg"+ "\"" + lineEnd + "Content-Type: application/octet-stream"+lineEnd+lineEnd;
byte[] composition = header.getBytes();
byte[] photoData = AttachPhotos.get(strAccidentPhoto[i]);
if(photoData != null)
{
_outputStream.write(startBytes);
_outputStream.write(composition);
_outputStream.write(photoData);
_outputStream.write(endBytes);
}
}
}
in my code I am using User-Agent as Profile/MIDP-2.0 Configuration/CLDC-1.0. Is this making any problem to post multiple files? Or is there any another way to post the data.
The code doesn't through any exception but is enable to post Image files only.
What I am missing in my code?
in my code I am using User-Agent as
Profile/MIDP-2.0
Configuration/CLDC-1.0. Is this making
any problem to post multiple files?
No. I would also recommend to not touch this header, because it is expected that the OS will set it in a most proper way.
UPDATE: Thanks to gnuf - I was incorrect saying "the OS will set it in a most proper way". It turns out there will be no such header at all, unless you set it. However I don't think this could be a reason, unless you instructed the server to reject requests if they don't have User-Agent header (which is unlikely).
Or is there any another way to post
the data.
There may be a range of reasons. Basically what you need is to send a POST request of a 'multipart/form-data' type. So you need to correspond to the format of a such request. Some most important possible issues in this case are:
Make sure you are using proper "Content-Length" and "Content-Type" headers. "Content-Type" should be "multipart/form-data; boundary=YOUR_ACTUAL_BOUNDARY"
Make sure you didn't forget to add 2 extra '-' chars to the ending boundary.
Related
I am working on the server that receives a file stream uploaded by multipart uploader.
But I got an additional WebKitFormBoundary.
If I remove it manually, it will work. So I tried the following code:
var fileStream = File.Create(#"C:\Users\myname\Desktop\myimage.png");
stream sr = new streamReader(myStream);
string myText = sr.ReadToEnd();
string newText = myText.Substring(myText.IndexOf("‰")); // remove header
byte[] byteArray = Encoding.ASCII.GetBytes(newText);
MemoryStream data = new MemoryStream(byteArray);
data.CopyTo(filestream);
If I use the above way to convert it to string, remove boundary and convert back to stream
the first character "‰" will become "?"
(ie. So ‰PNG will become ?PNG and the file becomes not readable.)
Any suggestions?
Where could I possible got wrong?
Thanks
This drove me nuts. Finally understood that if you have access to the request, you can access just the contents (with no header) like this:
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var file = await provider.Contents[0].ReadAsStreamAsync();
Hope this helps you, or someone with the same issue.
I have got the same issue but after investigating several blogs with applied several solutions, I got final working one. Please follow below code approach to fix it.
MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(filePath));
StreamReader streamReader = new StreamReader(memoryStream, Encoding.Default, true);
memoryStream.Seek(0, SeekOrigin.Begin);
string fileString = streamReader.ReadToEnd();
string fileData = fileString.Substring(0, fileString.IndexOf("\r\n\r\n") + 4);
string finalData = Regex.Replace(fileString, fileData, "");
var fileDataArr = Regex.Split(fileData, "\r\n|\r|\n").ToList();
var resultData = Regex.Replace(finalData, fileDataArr[0] + "--", "");
byte[] buffer = Encoding.Default.GetBytes(resultData);
Steps:
Convert your filedata into memory stream which can be used to read file content.
Use StreamReader to read file content and remove webkitformBoundary Header with default Encoding format.
Code To remove first 4 lines including webkitformBoundary from Top.
Code to remove webkitformBoundary from Footer.
Convert the string into Byte Array with default encoding format to maintain the file Encoding format.
Example:
WebKitFormBoundary Header
------WebKitFormBoundaryL1NUALe5NDrNt9S0 <br/>
Content-Disposition: form-data; name="userfile"; filename="BRtestfile1.pdf" <br/>
Content-Type: application/pdf <br/>
WebKitFormBoundary Footer
------WebKitFormBoundaryL1NUALe5NDrNt9S0-- <br/>
I'm receiving a standard request from an API. It looks something like this :
It's content type and length is :
But when this hits my Rails server, Rails responds with
The reason I'm bringing this up, is because the same request seems to work on SCORM Cloud's server. If I upload the exact same content to them, and watch it in the debugger, I see it send out an application/json statement with the same Request payload, but with no unexpected token error.
Does a Rails application/json request have to be written a certain way that differs from other servers? Is there a proper way to rewrite this line in Rack Middleware to prevent this error?
Update
The javascript :
function _TCDriver_XHR_request (lrs, url, method, data, callback, ignore404, extraHeaders) {
_TCDriver_Log("_TCDriver_XHR_request: " + url);
var xhr,
finished = false,
xDomainRequest = false,
ieXDomain = false,
ieModeRequest,
title,
ticks = ['/', '-', '\\', '|'],
location = window.location,
urlParts,
urlPort,
result,
extended,
until,
fullUrl = lrs.endpoint + url
;
urlParts = fullUrl.toLowerCase().match(/^(.+):\/\/([^:\/]*):?(\d+)?(\/.*)?$/);
// add extended LMS-specified values to the URL
if (lrs.extended !== undefined) {
extended = [];
for (var prop in lrs.extended) {
if(lrs.extended[prop] != null && lrs.extended[prop].length > 0){
extended.push(prop + "=" + encodeURIComponent(lrs.extended[prop]));
}
}
if (extended.length > 0) {
fullUrl += (fullUrl.indexOf("?") > -1 ? "&" : "?") + extended.join("&");
}
}
//Consolidate headers
var headers = {};
headers["Content-Type"] = "application/json";
headers["Authorization"] = lrs.auth;
if (extraHeaders !== null) {
for (var headerName in extraHeaders) {
headers[headerName] = extraHeaders[headerName];
}
}
//See if this really is a cross domain
xDomainRequest = (location.protocol.toLowerCase() !== urlParts[1] || location.hostname.toLowerCase() !== urlParts[2]);
if (! xDomainRequest) {
urlPort = (urlParts[3] === null ? ( urlParts[1] === 'http' ? '80' : '443') : urlParts[3]);
xDomainRequest = (urlPort === location.port);
}
//If it's not cross domain or we're not using IE, use the usual XmlHttpRequest
if (! xDomainRequest || typeof XDomainRequest === 'undefined') {
_TCDriver_Log("_TCDriver_XHR_request using XMLHttpRequest");
xhr = new XMLHttpRequest();
xhr.open(method, fullUrl, callback != null);
for (var headerName in headers) {
xhr.setRequestHeader(headerName, headers[headerName]);
}
}
//Otherwise, use IE's XDomainRequest object
else {
_TCDriver_Log("_TCDriver_XHR_request using XDomainRequest");
ieXDomain = true;
ieModeRequest = _TCDriver_GetIEModeRequest(method, fullUrl, headers, data);
xhr = new XDomainRequest ();
xhr.open(ieModeRequest.method, ieModeRequest.url);
}
Rails is being "helpful" here and assuming that the client is correctly using "Content-Type" and passing a value that actually matches that content type. In other words, the payload in the request has to be parseable JSON, and the value being passed is not valid JSON.
Which is an entirely reasonable thing for it to do when you are implementing an in house API that isn't intended for maximum interoperability. What Rails doesn't know is that an LRS' document storage is supposed to be "dumb" and basically allow the client to shove whatever it wants in and get whatever it wants out, which is why SCORM Cloud accepts the request, basically it just stores the content type and the contents, and then regurgitates them as is on request.
The code you pasted is from a very old library that has poor implementation of Content-Type headers. If this code is found anywhere other than in a relatively old version of a piece of content from one of the major e-learning authoring tools then it should be updated to use a recent version of TinCanJS and improve the content type handling.
As far as making this work on Rails, sorry I don't have that much experience with it. Presumably there is a switch or something to turn off automatic request body parsing, at least that's what most other frameworks I've used have.
Does a Rails application/json request have to be written a certain way that differs from other servers?
Not that I know of no.
Is there a proper way to rewrite this line in Rack Middleware to prevent this error?
There might a way yes, maybe even without rack middlewares, although it's quite hard to help you without an actual request to work with.
I have an oddity I hope someone can help with, I have an application which is uploading a reasonably long string as a post parameter, I know the web request is right because it works fine with PHP.
With Rails 4 though it seems to just chop off the string at the same point every time, yet I can't find any documentation that indicates this is normal behavior i'm assigning it like so:
mystring= params[:post_xml]
if I do the following:
mystring = request.body.read
It works fine!
Any ideas?
EDIT For clarity here's my C# request code its on port 3001 as thats the test port for rails
HttpWebRequest httpWReq = (HttpWebRequest)WebRequest.Create("http://mydomain.com:3001/api/new");
ASCIIEncoding encoding = new ASCIIEncoding();
var textFromDoc = Globals.ThisAddIn.Application.ActiveDocument.Content.Text;
// string postData = "content=" + textFromDoc;
//byte[] data = encoding.GetBytes(postData);
byte[] data = Encoding.UTF8.GetBytes(textFromDoc);
httpWReq.Method = "POST";
httpWReq.ContentType = "text/xml; encoding='utf-8'";
//"application/x-www-form-urlencoded";
httpWReq.ContentLength = data.Length;
using (Stream stream = httpWReq.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
HttpWebResponse response = (HttpWebResponse)httpWReq.GetResponse();
string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
System.Windows.Forms.MessageBox.Show(responseString);
}
catch (System.Exception excep)
{
System.Windows.Forms.MessageBox.Show(excep.Message);
}
ActionController::StrongParameters#params is very different from ActionDispatch::Request#body. These two methods serve different purposes.
Check out Chapter 4: Parameters in the Action Controller Overview to learn more.
I have an ASP.net application that allows the users to report bugs and attach files. The bug together with its detail and attachments should be saved in FogBugz.
I have managed to create everything except the file attachment part.
here is my code:
private void NewCaseWithFile()
{
string fbUrl = "https://test.fogbugz.com/api.asp";
string fbToken = logInFogBugz();
string param = "";
param += "cmd=new";
param += "&token=" + fbToken;
param += "&sTags=" + "OnlineService,";
param += "&sTitle=" + "Testing";
param += "&sEvent=" + "This case is being created from Visual Studio";
param += "&nFileCount=" + "1";
param += "&File1=" + "Picture.png";
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(fbUrl + "?" + param);
httpWebRequest.Method = WebRequestMethods.Http.Post;
httpWebRequest.ContentType = "multipart/form-data";
httpWebRequest.Accept = "application/xml";
httpWebRequest.ContentLength = 0;
HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
StreamReader streamReader = new StreamReader(response.GetResponseStream());
XDocument doc = XDocument.Load(streamReader);
}
I have tried all instructions under "Editing Cases" but it did not help. In fact I have no idea what are File 1, File 2 and how to send them to FogBugz.
Can anyone help me with this?
Many thanks!
File1 should be specified in the body of your multipart/form-data post (not as a querystring parameter).
You actually have to specify all the bytes in the file.
There's an answer on fogbugz.stackexchange.com as well as a C# FogBugz API wrapper that will handle all the parts for you.
The form parts in the body of your post would look like
--sdfjdsjsdflk SOME BOUNDARY--
Content-Disposition: form-data; name="File1"; filename="foo.jpg"
Content-Transfer-Encoding: base64
Content-Type: image/png
slfkajdflksjflajfdj
sldfjsd;aljfds
these are actual data bytes from the foo.jpg file
slfkjdsfljds
sdflajsdfs
Or you can look at this question which points to an RFC with an example.
I have been looking for a way to remove an attachment from Jira using the SOAP Api, but it seems that this is not possible natively, and I would prefer not having to implement a new plugin for Jira, as suggested in the accepted answer to this question, or recompiling the existing plugin to support this as mentioned here.
This answer to the abovementioned question seems to do exactly what I want, but alas, I can't get i to work. The response i get is an error stating that:
XSRF Security Token Missing
JIRA could not complete this action due to a missing form token.
You may have cleared your browser cookies, which could have resulted in the expiry of your current form token. A new form token has been reissued.
As I am using Asp.Net MVC C#, I have used the code from the answer, as is, with only the server url adjusted, as well as with different credentials (a Jira user) and the username/password passed through as request parameters using:
os_username=jirausername&os_password=xxxxxxx
The code I am currently using is as follows:
public void RemoveAttachment(string issueid, string attachmentid)
{
using (System.Net.WebClient client = new System.Net.WebClient())
{
//Compute jira server base url from WS url
string baseUrl = _service.Url.Substring(0, _service.Url.IndexOf("/rpc/"));
//Compute complete attachment url
string attachmenturl = baseUrl + "/secure/DeleteAttachment.jspa?id=" +
issueid + "&deleteAttachmentId=" + attachmentid;
client.Credentials = new System.Net.NetworkCredential("jirausername", "xxxxxxx");
string response = client.DownloadString(attachmenturl);
}
}
I ended up using a method that first requests the deletion confirmation form, then extracts a required token from the form, and finally posts something equivalent to the form content in order to delete the attachment. Code below.
public void RemoveAttachment(string issueid, string attachmentid)
{
//Compute jira server base url from WS url
string baseUrl = _service.Url.Substring(0, _service.Url.IndexOf("/rpc/"));
//Compute complete attachment deletion confirm url
string confirmurl = baseUrl + "/secure/DeleteAttachment!default.jspa?id=" +
issueid + "&deleteAttachmentId=" + attachmentid + "&os_username=jirauser&os_password=xxxxxx";
//Create a cookie container to maintain the xsrf security token cookie.
CookieContainer jiracontainer = new CookieContainer();
//Create a get request for the page containing the delete confirmation.
HttpWebRequest confirmrequest = (HttpWebRequest)WebRequest.Create(confirmurl);
confirmrequest.Credentials = System.Net.CredentialCache.DefaultCredentials;
confirmrequest.CookieContainer = jiracontainer;
//Get the response and the responsestream.
WebResponse confirmdeleteresponse = confirmrequest.GetResponse();
Stream ReceiveStream = confirmdeleteresponse.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader confirmreader = new StreamReader(ReceiveStream);
// Read the content.
string confirmresponse = confirmreader.ReadToEnd();
//Create a regex to extract the atl/xsrf token from a hidden field. (Might be nicer to read it from a cookie, which should also be possible).
Regex atl_token_matcher = new Regex("<input[^>]*id=\"atl_token\"[^>]*value=\"(?<token>\\S+)\"[^>]*>", RegexOptions.Singleline);
Match token_match = atl_token_matcher.Match(confirmresponse);
if (token_match.Success)
{
//If we found the token get the value.
string token = token_match.Groups["token"].Value;
//Compute attachment delete url.
string deleteurl = baseUrl + "/secure/DeleteAttachment.jspa";
//Construct form data.
string postdata = "atl_token=" + HttpContext.Current.Server.UrlEncode(token) + "&id=" + issueid + "&deleteAttachmentId=" + attachmentid + "&Delete=Delete&os_username=jirauser&os_password=xxxxxx";
//Create a post request for the deletion page.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(deleteurl);
request.KeepAlive = false;
request.CookieContainer = jiracontainer; // Remember to set the cookiecontainer.
request.ProtocolVersion = HttpVersion.Version10;
request.Method = "POST";
//Turn our request string into a byte stream
byte[] postBytes = Encoding.ASCII.GetBytes(postdata);
//Make sure you specify the proper type.
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postBytes.Length;
Stream requestStream = request.GetRequestStream();
//Send the post.
requestStream.Write(postBytes, 0, postBytes.Length);
requestStream.Close();
//Get the response.
WebResponse deleteresponse = request.GetResponse();
// Open the responsestream using a StreamReader for easy access.
StreamReader deleteresponsereader = new StreamReader(deleteresponse.GetResponseStream());
// Read the content.
string deleteresponsecontent = deleteresponsereader.ReadToEnd();
// do whatever validation/reporting with the response...
}
else
{
//We couldn't find the atl_token. Throw an error or something...
}
}
Edit:
Same thing works for removing comments. Replace 'attachment' with 'comment' and 'deleteAttachmentId' with 'commentId' and you should be good to go.