简体   繁体   English

如何使用REST API将附件发布到JIRA?

[英]How to POST attachment to JIRA using REST API?

How to POST attachment to JIRA using JIRA REST API and HttpWebRequest in C#? 如何在C#中使用JIRA REST API和HttpWebRequest将附件发布到JIRA?

From the documentation under /rest/api/2/issue/{issueIdOrKey}/attachments : / rest / api / 2 / issue / {issueIdOrKey} / attachments下文档

POST 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. 媒体类型的multipart / form-data在RFC 1867中定义。大多数客户端库都有类,使得处理多部分帖子变得简单。 For instance, in Java the Apache HTTP Components library provides a MultiPartEntity that makes it simple to submit a multipart POST. 例如,在Java中,Apache HTTP Components库提供了一个MultiPartEntity,使得提交多部分POST变得简单。

In order to protect against XSRF attacks, because this method accepts multipart/form-data, it has XSRF protection on it. 为了防止XSRF攻击,因为此方法接受multipart / form-data,所以它具有XSRF保护。 This means you must submit a header of X-Atlassian-Token: nocheck with the request, otherwise it will be blocked. 这意味着您必须提交X-Atlassian-Token标头:nocheck请求,否则将被阻止。

The name of the multipart/form-data parameter that contains attachments must be "file" 包含附件的multipart / form-data参数的名称必须是“file”

A simple example to upload a file called "myfile.txt" to issue REST-123: 上传名为“myfile.txt”的文件以发布REST-123的简单示例:

curl -D- -u admin:admin -X POST -H "X-Atlassian-Token: nocheck" -F "file=@myfile.txt" http://myhost.test/rest/api/2/issue/TEST-123/attachments curl -D- -u admin:admin -X POST -H“X-Atlassian-Token:nocheck”-F“file=@myfile.txt” http://myhost.test/rest/api/2/issue/TEST -123 /附件


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... 但它返回404错误...

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! PS:thanks2mod删除我以前的帖子! nice ... 很好......

There was a couple of mistakes in the OP's code. OP的代码中有一些错误。

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. 使用@mabu提供的代码段和我在http://www.briangrinstead.com/blog/multipart-form-post-in-c上找到的代码,这里有一个*功能**代码块上传到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. 我真的不想处理所有那些boundary东西,所以这是我的镜头。 This works against Confluence whose API looks identical to Jira. 这适用于Confluence,其API看起来与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). 感谢ASP.NET WebApi的 Michael Teper :如何使用WebApi HttpClient和Jeff Caron(上图) 执行带文件上传的多部分帖子

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);
    }
}

You're also able to do with Restsharp as follow 您还可以使用Restsharp,如下所示

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Jira
using RestSharp;
using RestSharp.Authenticators;

namespace Jira
{
    class Program
    {
      static void Main(string[] args)
      {
        var client = new RestClient("http://{URL}/rest/api/2");
        var request = new RestRequest("issue/", Method.POST);

        client.Authenticator = new HttpBasicAuthenticator("user", "pass");

        var issue = new Issue
        {
            fields =
                new Fields
                {
                    description = "Issue Description",
                    summary = "Issue Summary",
                    project = new Project { key = "KEY" }, 
                    issuetype = new IssueType { name = "ISSUE_TYPE_NAME" }
                }
        };

        request.AddJsonBody(issue);

        var res = client.Execute<Issue>(request);

        if (res.StatusCode == HttpStatusCode.Created)
        {
            Console.WriteLine("Issue: {0} successfully created", res.Data.key);

            #region Attachment            
            request = new RestRequest(string.Format("issue/{0}/attachments", res.Data.key), Method.POST);

            request.AddHeader("X-Atlassian-Token", "nocheck");

            var file = File.ReadAllBytes(@"C:\FB_IMG_1445253679378.jpg");

            request.AddHeader("Content-Type", "multipart/form-data");
            request.AddFileBytes("file", file, "FB_IMG_1445253679378.jpg", "application/octet-stream");

            var res2 = client.Execute(request);

            Console.WriteLine(res2.StatusCode == HttpStatusCode.OK ? "Attachment added!" : res2.Content);
            #endregion
        }
        else
            Console.WriteLine(res.Content);
      }
    }

    public class Issue
    {
        public string id { get; set; }
        public string key { get; set; }
        public Fields fields { get; set; }
    }

    public class Fields
    {
        public Project project { get; set; }
        public IssueType issuetype { get; set; }
        public string summary { get; set; }
        public string description { get; set; }        
    }

    public class Project
    {
        public string id { get; set; }
        public string key { get; set; }
    }

    public class IssueType
    {
        public string id { get; set; }
        public string name { get; set; }
    }
}

The whole code is on gist https://gist.github.com/gandarez/c2c5b2b27dbaf62a0d634253529bcb59 整个代码在gist上https://gist.github.com/gandarez/c2c5b2b27dbaf62a0d634253529bcb59

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM