9

如何在 C# 中使用 JIRA REST API 和 HttpWebRequest 将附件发布到 JIRA?

/rest/api/2/issue/{issueIdOrKey}/attachments 下的文档中

邮政

为问题添加一个或多个附件。

该资源需要一个多部分的帖子。media-type multipart/form-data 在 RFC 1867 中定义。大多数客户端库都有使处理多部分帖子变得简单的类。例如,在 Java 中,Apache HTTP 组件库提供了一个 MultiPartEntity,它使得提交多部分 POST 变得简单。

为了防止 XSRF 攻击,因为这个方法接受 multipart/form-data,所以它上面有 XSRF 保护。这意味着您必须在请求中提交 X-Atlassian-Token: nocheck 的标头,否则将被阻止。

包含附件的 multipart/form-data 参数的名称必须是“file”

上传名为“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/附件


我有

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

但它返回一个 404 错误...

4

4 回答 4

6

解决了你的问题:

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删除我以前的帖子!好的 ...

于 2012-09-27T14:12:16.827 回答
6

OP 的代码中有几个错误。

使用@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();
        }
    }
}
于 2013-08-28T13:22:23.720 回答
4

我真的不想处理所有这些boundary东西,所以这是我的尝试。这适用于其 API 看起来与 Jira 相同的Confluence 。

感谢ASP.NET WebApi 的 Michael Teper:how to perform a multipart post with file upload using WebApi HttpClient and 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);
    }
}
于 2016-08-16T12:34:33.377 回答
0

您还可以按如下方式使用 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; }
    }
}

整个代码在要点上https://gist.github.com/gandarez/c2c5b2b27dbaf62a0d634253529bcb59

于 2016-08-25T19:22:01.350 回答