2012-08-09 240 views
6

¿Cómo se PUEDE adjuntar a JIRA utilizando JIRA REST API y HttpWebRequest en C#?¿Cómo se PUEDE adjuntar a JIRA utilizando la API REST?

Desde el documentation under /rest/api/2/issue/{issueIdOrKey}/attachments:

POSTAL

Añadir uno o más archivos adjuntos a un problema.

Este recurso espera una publicación de varias partes. El tipo de medio de datos/formulario-datos se define en RFC 1867. La mayoría de las bibliotecas de clientes tienen clases que hacen que tratar con publicaciones de varias partes sea simple. Por ejemplo, en Java, la biblioteca de componentes Apache HTTP proporciona una MultiPartEntity que hace que sea sencillo enviar una POST de varias partes.

Para protegerse contra ataques XSRF, ya que este método acepta multipart/form-data, tiene protección XSRF en él. Esto significa que debe enviar un encabezado de X-Atlassian-Token: nocheck con la solicitud; de lo contrario, se bloqueará.

El nombre del parámetro multipart/form-data que contiene archivos adjuntos deben ser "archivo"

Un ejemplo sencillo para cargar un archivo llamado "miarchivo.txt" para emitir REST-123:

rizo -D- -u administrador: admin -X POSTAL -H "X-Atlassian-Token: nocheck" -F "[email protected]" http://myhost.test/rest/api/2/issue/TEST-123/attachments


tengo

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 [email protected]" + 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(); 
} 

pero devuelve un error 404 ...

+0

Lo RESTO está usando? –

+0

¿También puede proporcionar un enlace a su presupuesto de la documentación? –

+0

[API REST JIRA 5.1.2] (http://docs.atlassian.com/jira/REST/latest/) –

Respuesta

6

resuelven su problema:

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 suprimir mi post anterior! nice ...

+0

¡Gracias! Muy útil. Solo obtengo una excepción al final en var data = JObject.Parse (responseData); Dice que no es un objeto json. Pero inspeccionar el Datasheet de respuesta definitivamente lo es. ¿¿Algunas ideas?? – Ronald

6

Hubo un par de errores en el código del OP.

Con el fragmento provisto por @mabu y el código que encontré en http://www.briangrinstead.com/blog/multipart-form-post-in-c, aquí hay un bloque de código * funcional ** para cargar datos adjuntos a 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(); 
     } 
    } 
} 
2

Realmente no quiero lidiar con todo lo que boundary cosas, así que aquí está mi oportunidad. Esto funciona en contra de Confluence cuya API es idéntica a Jira.

Gracias a Michael Teper en ASP.NET WebApi: how to perform a multipart post with file upload using WebApi HttpClient y Jeff Caron (arriba).

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

También es capaz de hacer con Restsharp de la siguiente manera

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

Todo el código es el marco GIST https://gist.github.com/gandarez/c2c5b2b27dbaf62a0d634253529bcb59