Community

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
ksz
New Member

multipart/form-data upload receives bad request

I'm trying to upload a file to Canvas using the API. I'm on the second step where you construct the multipart form and POST it but I'm getting a 400 Bad Request error every time with nothing else to give me a clue as to what is wrong. 

I post to the url I receive from the first step.

Anybody have an idea as to why it's giving me a bad request?

How can I find out why it's giving me a 400 Bad Request? 

Thanks in advance.

Here is my multipart/form-data before it's converted to binary:

Content-Type: multipart/form-data; boundary=----------b3323d8480cf4a5f89f36e1680263e49

------------b3323d8480cf4a5f89f36e1680263e49\r\n
Content-Disposition: form-data; name=\"fileToUpload\"; filename=\"profile_picture.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"key\"\r\n\r\naccount_<account id>/attachments/<account id>/profile_picture.jpg
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"x-amz-credential\"\r\n\r\nAKIAJKYS55LTKAJVWK3Q/20181113/eu-west-1/s3/aws4_request
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"x-amz-algorithm\"\r\n\r\nAWS4-HMAC-SHA256
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"x-amz-date\"\r\n\r\n20181113T075946Z
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"Filename\"\r\n\r\nprofile_picture.jpg
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"acl\"\r\n\r\nprivate
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data;name=\"Policy\"\r\n\r\neyJleHBpcmF0aW9uIjoiMjAxOC0xMS0xM1QwODoyOTo0NloiLCJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJpbnN0cnVjdHVyZS11cGxvYWRzLWV1In0seyJrZXkiOiJhY2NvdW50XzgxMDQwMDAwMDAwMDAwMDAxL2F0dGFjaG1lbnRzLzM3MTE0NS9wcm9maWxlX3BpY3R1cmUuanBnIn0seyJhY2wiOiJwcml2YXRlIn0sWyJzdGFydHMtd2l0aCIsIiRGaWxlbmFtZSIsIiJdLFsiY29udGVudC1sZW5ndGgtcmFuZ2UiLDEsMTA3Mzc0MTgyNDBdLHsieC1hbXotY3JlZGVudGlhbCI6IkFLSUFKS1lTNTVMVEtBSlZXSzNRLzIwMTgxMTEzL2V1LXdlc3QtMS9zMy9hd3M0X3JlcXVlc3QifSx7IngtYW16LWFsZ29yaXRobSI6IkFXUzQtSE1BQy1TSEEyNTYifSx7IngtYW16LWRhdGUiOiIyMDE4MTExM1QwNzU5NDZaIn0seyJzdWNjZXNzX2FjdGlvbl9yZWRpcmVjdCI6Imh0dHBzOi8vZGlzYWJyb2FkLnRlc3QuaW5zdHJ1Y3R1cmUuY29tL2FwaS92MS9maWxlcy8zNzExNDUvY3JlYXRlX3N1Y2Nlc3M/dXVpZD1aQ1FzcEpqY0l2STN6UEtBTFl1a3JqMlJPY1JPNzNDRFdpMnY5Y0N1In0seyJjb250ZW50LXR5cGUiOiJpbWFnZS9qcGVnIn1dfQ==
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"x-amz-signature\"\r\n\r\ndd7cde0c122f6f5d437d2b6b2b37619dc8cbe4acccad21d9a11da1d809cec853
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\nhttps://disabroad.test.instructure.com/api/v1/files/371145/create_success?uuid=ZCQspJjcIvI3zPKALYukr...
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"content-type\"\r\n\r\nimage/jpeg
------------b3323d8480cf4a5f89f36e1680263e49\r\n

Content-Disposition: form-data; name=\"file\"\r\n\r\nprofile_picture.jpg\r\n
------------b3323d8480cf4a5f89f36e1680263e49--\r\n

0 Kudos
4 Replies
pklove
Community Champion

I think the data you have shown above has the upload_params from the JSON response from the first step.  Are you also posting the actual file as the last parameter?  Can you show us the code that you are using to do the post?

Hi Peter, thanks for the reply.

below is my code. The file parameter is the last one being attached to the form. No matter what I try I keep getting Bad Request 400. The CanvasFileUploadRequest  object contains the parameters returned from the first step. I can see everything is being added successfully.

public string UploadFile(CanvasFileUploadRequest response, byte[] fileByteArray, string fileName, string contentType)
{

var values = new NameValueCollection();
var files = new Dictionary<string, byte[]>();

values.Add("key", response.UploadParams["key"]);

foreach (var key in response.UploadParams.Keys)
{
if (key == "key") continue;

values.Add(key, response.UploadParams[key]);
}

files.Add("profilePicture", fileByteArray);

sendHttpRequest(response.UploadUrl, values, files);

return string.Empty;
}

//method that constructs the multi part form

private static string sendHttpRequest(string url, NameValueCollection values, Dictionary<string, byte[]> files)
{
string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
// The first boundary
byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
// The last boundary
byte[] trailer = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
// The first time it itereates, we need to make sure it doesn't put too many new paragraphs down or it completely messes up poor webbrick
byte[] boundaryBytesF = System.Text.Encoding.ASCII.GetBytes("--" + boundary + "\r\n");

// Create the request and set parameters
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.Method = "POST";
request.KeepAlive = true;
//request.Credentials = CredentialCache.DefaultCredentials;

// Get request stream
Stream requestStream = request.GetRequestStream();

foreach (string key in values.Keys)
{
// Write item to stream
byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}", key, values[key]));
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
requestStream.Write(formItemBytes, 0, formItemBytes.Length);
}

if (files != null)
{
foreach (string key in files.Keys)
{
//byte[] buffer = new byte[2048];
byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n", key, files[key]));
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
requestStream.Write(formItemBytes, 0, formItemBytes.Length);

var file = files[key];

requestStream.Write(file, 0, file.Length);
}
}

// Write trailer and close stream
requestStream.Write(trailer, 0, trailer.Length);
requestStream.Close();

using (StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream()))
{
return reader.ReadToEnd();
};
}

pklove
Community Champion

Ah, sorry, not one of my regular languages.

All could suggest is dumping the parameters from the first call in a format you could try with command-line curl.  That would distinguish whether its something to do with your code, or something else.

Peter, thanks for the suggestion. I don't know what I didn't think of that!

I can upload files using curl though command line so clearly the problem is somewhere with my construction of the multi-part form.

Kris