How to POST attachment to JIRA using JIRA REST API and HttpWebRequest in C#?
From the documentation under /rest/api/2/issue/{issueIdOrKey}/attachments:
POST
Add one or more attachments to an issue.
This resource expects a multipart post. The media-type multipart/form-data is defined in RFC 1867. Most client libraries have classes that make dealing with multipart posts simple. For instance, in Java the Apache HTTP Components library provides a MultiPartEntity that makes it simple to submit a multipart POST.
In order to protect against XSRF attacks, because this method accepts multipart/form-data, it has XSRF protection on it. This means you must submit a header of X-Atlassian-Token: nocheck with the request, otherwise it will be blocked.
The name of the multipart/form-data parameter that contains attachments must be "file"
A simple example to upload a file called "myfile.txt" to issue REST-123:
curl -D- -u admin:admin -X POST -H "X-Atlassian-Token: nocheck" -F "[email protected]" http://myhost.test/rest/api/2/issue/TEST-123/attachments
I have
foreach (JIRAAttachments attachm in attachments.attachments)
{
request = HttpWebRequest.Create(
logInformation.GetUri() + "/rest/api/2/issue/" + key + "/attachments"
) as HttpWebRequest;
request.Headers.Add("Authorization: Basic " + logInformation.GetEncodeAuthentication());
request.Method = "POST";
request.ContentType = "multipart/form-data";
request.Headers.Add("X-Atlassian-Token: nocheck file=@" + Path.GetFullPath(@"..\Attachments\" + attachm.filename));
request.KeepAlive = true;
request.Proxy = wp;
response = (HttpWebResponse)request.GetResponse();
Stream s = response.GetResponseStream();
FileStream fs = new FileStream(Path.GetFullPath(@"..\Attachments\" + attachm.filename), FileMode.Open);
byte[] write = new byte[256];
int count = fs.Read(write, 0, write.Length);
while (count > 0)
{
s.Write(write, 0, count);
count = fs.Read(write, 0, write.Length);
}
fs.Close();
s.Close();
response.Close();
}
but it returns a 404 error...
solved your problem:
var boundary = string.Format("----------{0:N}", Guid.NewGuid());
System.IO.MemoryStream content = new MemoryStream();
var writer = new StreamWriter(content);
foreach (var att in attachments)
{
writer.WriteLine("--{0}", boundary);
writer.WriteLine("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"", "file", Path.GetFileName(att["filename"]));
writer.WriteLine("Content-Type: {0}", att.ContentType);
writer.WriteLine();
writer.Flush();
att.Stream.CopyTo(content);
writer.WriteLine();
}
writer.WriteLine("--" + boundary + "--");
writer.Flush();
content.Seek(0, SeekOrigin.Begin);
HttpWebRequest oRequest = null;
oRequest = (HttpWebRequest)HttpWebRequest.Create(string.Format(RestBaseURI + "issue/{0}/attachments", item.Key));
oRequest.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
oRequest.Method = "POST";
oRequest.Headers.Add("Authorization", AuthData);
oRequest.Headers.Add("X-Atlassian-Token", "nocheck");
oRequest.UseDefaultCredentials = true;
oRequest.KeepAlive = true;
oRequest.ContentLength = content.Length;
using (var oStream = oRequest.GetRequestStream())
{
content.CopyTo(oStream);
}
using (var oResponse = (HttpWebResponse)oRequest.GetResponse())
{
using (var reader = new StreamReader(oResponse.GetResponseStream()))
{
var responseData = reader.ReadToEnd();
var data = JObject.Parse(responseData);
}
}
PS: thanks2mod to delete my previous post! nice ...
There was a couple of mistakes in the OP's code.
With the snippet provided by @mabu and the code I found on http://www.briangrinstead.com/blog/multipart-form-post-in-c, here's a *functional** Code Block to Upload attachment to Jira.
public bool AddAttachments(string issueKey, IEnumerable<string> filePaths)
{
string restUrl = Jira.FormatRestUrl(m_JiraId, true);
string issueLinkUrl = String.Format("{0}/issue/{1}/attachments", restUrl, issueKey);
var filesToUpload = new List<FileInfo>();
foreach (var filePath in filePaths)
{
if (!File.Exists(filePath))
{
Jira.LogError("File '{0}' doesn't exist", filePath);
return false;
}
var file = new FileInfo(filePath);
if (file.Length > 10485760) // TODO Get Actual Limit
{
Jira.LogError("Attachment too large");
return false;
}
filesToUpload.Add(file);
}
if (filesToUpload.Count <= 0)
{
Jira.LogWarning("No file to Upload");
return false;
}
return PostMultiPart(issueLinkUrl, filesToUpload);
}
private Boolean PostMultiPart(string restUrl, IEnumerable<FileInfo> filePaths)
{
HttpWebResponse response = null;
HttpWebRequest request = null;
try
{
var boundary = string.Format("----------{0:N}", Guid.NewGuid());
var content = new MemoryStream();
var writer = new StreamWriter(content);
foreach (var filePath in filePaths)
{
var fs = new FileStream(filePath.FullName, FileMode.Open, FileAccess.Read);
var data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
fs.Close();
writer.WriteLine("--{0}", boundary);
writer.WriteLine("Content-Disposition: form-data; name=\"file\"; filename=\"{0}\"", filePath.Name);
writer.WriteLine("Content-Type: application/octet-stream");
writer.WriteLine();
writer.Flush();
content.Write(data, 0, data.Length);
writer.WriteLine();
}
writer.WriteLine("--" + boundary + "--");
writer.Flush();
content.Seek(0, SeekOrigin.Begin);
request = WebRequest.Create(restUrl) as HttpWebRequest;
if (request == null)
{
Jira.LogError("Unable to create REST query: {0}", restUrl);
return false;
}
request.Method = "POST";
request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
request.Accept = "application/json";
request.Headers.Add("Authorization", "Basic " + m_EncodedCredential);
request.Headers.Add("X-Atlassian-Token", "nocheck");
request.ContentLength = content.Length;
using (Stream requestStream = request.GetRequestStream())
{
content.WriteTo(requestStream);
requestStream.Close();
}
using (response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
{
var reader = new StreamReader(response.GetResponseStream());
Jira.LogError("The server returned '{0}'\n{1}", response.StatusCode, reader.ReadToEnd());
return false;
}
return true;
}
}
catch (WebException wex)
{
if (wex.Response != null)
{
using (var errorResponse = (HttpWebResponse)wex.Response)
{
var reader = new StreamReader(errorResponse.GetResponseStream());
Jira.LogError("The server returned '{0}'\n{1}).", errorResponse.StatusCode, reader.ReadToEnd());
}
}
if (request != null)
{
request.Abort();
}
return false;
}
finally
{
if (response != null)
{
response.Close();
}
}
}
I really didn't want to deal with all that boundary
stuff, so here's my shot at it. This works against Confluence whose API looks identical to Jira.
Thanks to Michael Teper at ASP.NET WebApi: how to perform a multipart post with file upload using WebApi HttpClient and Jeff Caron (above).
var contents = "some long HTML that I wanted to upload";
var fileName = "Some fancy file name.html";
using (var client = new HttpClient())
{
var uri = new Uri(URL);
client.BaseAddress = new Uri(URL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = authorization;
client.DefaultRequestHeaders.Add("X-Atlassian-Token", "nocheck");
var uriPath = String.Format(AttachmentPath, pageId);
var content = new MultipartFormDataContent();
var fileContent = new StringContent(contents);
// also tested to work:
// var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes(contents));
content.Add(fileContent, "file", fileName);
var response = await client.PostAsync(uriPath, content);
if (response.IsSuccessStatusCode)
{
return TaskResult.Success(null, response.ReasonPhrase);
}
else
{
return TaskResult.Failure("Service responded with Status Code: " + response.StatusCode + Environment.NewLine + "Reason Phrase: " + response.ReasonPhrase);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With