I am writing a plugin which needs to access attached files and look into them, as well as create attachments.
So I have all the "managers" I need, I get an issue object, and I call issue.getAttachments() which returns com.atlassian.jira.issue.attachment.Attachment objects.
How do I get the actual file data? The Attachment object has the following getters:
getAuthor() : String
getCreated() : Timestamp
getFilename() : String (this one is the short filename, not the path, such as "image.jpg")
getFilesize() : Long
getGenericValue() : GenericValue (not deprecated, but probably should be soon)
getId() : Long
getIssue : GenericValue (deprecated)
getIssueObject : Issue
getMimetype() : String
getProperties : PropertySet
getGenericValue / getString / getLong (the GV accessors)
getTimestamp(String) : Timestamp
None of these let me get at the actual attachment data... Currently, I am doing something like this:
String attachmentBasePath = applicationProperties.getString(APKeys.JIRA_PATH_ATTACHMENTS); String pkey = attachment.getIssueObject().getProjectObject().getKey(); String ikey = attachment.getIssueObject().getKey(); StringBuilder sb = new StringBuilder() sb.append(attachmentBasePath); sb.append(File.separator); sb.append(pkey); sb.append(File.separator); sb.append(ikey); sb.append(File.separator); sp.append(attachment.getId()); // * return sb.toString();
Furthermore, if you look at the * above, in jira 4.X you used to also have to append "_image.jpg" or whatever for the filename, this has apparently changed in jira 5.X. Clearly this isn't portable or safe and can break at any time JIRA changes its internals.
Even worse, I've determiend that APKeys.JIRA_PATH_ATTACHEMNTS stored in the DB does not get updated, so for example, if you use "create-home-zip" with the plugin SDK to save a database, then restore it from some other path later, all your tests/plugin will break because it can't find the attachments anymore. Production JIRAs probably don't change paths very often, but that makes this no less terrible. How do I do this? =(
Thanks!
-Carl
Community moderators have prevented the ability to post new answers.
I found the canonical implementation I think.
/** * Returns the physical File for the given Attachment. * This method performs better as it does not need to look up the issue object. * * @param issue the issue the attachment belongs to. * @param attachment the attachment. * @return the file. * @throws DataAccessException on failure getting required attachment info. */ public static File getAttachmentFile(Issue issue, Attachment attachment) throws DataAccessException { final File attachmentDir = getAttachmentDirectory(issue); return getAttachmentFile(AttachmentAdapter.fromAttachment(attachment), attachmentDir); }
This is in the AttachmentUtils class. I will use this and hopefully it won't break... but I'd still really like to see the atlassian dev's comments about the JIRA_PATH_ATTACHMENTS ApplicationProperty not being updated, as looking through the code I see it still uses that.
Another problem with this is, the AttachmentUtils method is static, meaning you can't write tests around it (if you call them outside of jira, you get an IllegalStateException sine the ComponentManager isn't initialized - this code should be moved to a non-static manager). I'd really like to have a jira dev weigh in, and I can file a jira issue if you like.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi everyone, I know this thread is old but here is an example implementation of InputStreamConsumer.
Hope it helps!
public class FileInputStreamConsumer implements InputStreamConsumer<File>{ private final String filename; public FileInputStreamConsumer(String filename) { this.filename = filename; } @Override public File withInputStream(InputStream is) throws IOException { final File f = File.createTempFile(FilenameUtils.getBaseName(filename), FilenameUtils.getExtension(filename)); StreamUtils.copy(is, new FileOutputStream(f)); return f; } }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Then you can use it as you like to get files from attachement:
for (final Attachment a : attachs) { File f = null; try { f = am.streamAttachmentContent(a, new FileInputStreamConsumer(a.getFilename())); } catch (final IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (f != null && f.exists()) { paths.add(f); } }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
OMG! Thank you!!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
And, for Jira 6.2
String filePath = PathUtils.joinPaths(ComponentAccessor.getAttachmentPathManager().getDefaultAttachmentPath(), issue.getProjectObject().getKey(), issue.getKey(), attachment.getId().toString()); File file = new File(filePath); if (file.exists()) { // attachments file exists... }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Since JIRA 6.3 there is also the bucket number in the middle of the complete path.
Using the FileAttachments class we could write:
bucketNumber = FileAttachments.computeIssueBucketDir(issue.getKey())
String filePath = PathUtils.joinPaths(ComponentAccessor.getAttachmentPathManager().getDefaultAttachmentPath(),
issue.getProjectObject().getKey(),
bucketNumber,
issue.getKey(),
attachment.getId().toString());
or better:
String attachmentPath = FileAttachments.getAttachmentDirectoryForIssue(new File(ComponentAccessor.getAttachmentPathManager().getDefaultAttachmentPath());
String filePath = PathUtils.joinPaths(attachmentPath,
issue.getProjectObject().getKey(),
issue.getKey()).toString(),
attachment.getId().toString());
in this last case you just need to know the project key , the issue key and the attachment id and you don't care about the bucket number
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
If project key has been modified by an administrator, attachement path keep the old project key and the "joinPaths(pathManager.attachmentPath, issue.projectObject.key, issue.key, attachment.id.toString())" proposed by Jamie return a path with the new project key. So it does not work.
I do it with AttachmentUtils which return the correct path and File:
File attachmentFile = AttachmentUtils.getAttachmentFile(attachment);
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello,
I've been using this way to retrieve the attatchment File object when needed. Maybe this method can help you too;
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.attachment.FileSystemAttachmentDirectoryAccessor import com.atlassian.jira.issue.Issue File getAttatchmentFile(Issue issue,String attatchmentId){ return ComponentAccessor.getComponent(FileSystemAttachmentDirectoryAccessor.class).getAttachmentDirectory(issue).listFiles().find({ File it-> it.getName().equals(attatchmentId) }); }
Regards,
Italo Qualisoni
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Trying to build a path to the attachment yourself is brittle, as JIRA can decide on a different directory structure (which seems to have happened with JIRA 7).
A better solution would be using AttachmentUtils.getAttachmentFile(attachment)
as @christophe rousset suggested:
File attachmentFile = AttachmentUtils.getAttachmentFile(attachment);
However that method has been deprecated since 6.1. It still exists in 7.1, but if you just want to get the attachment content, it is suggested to use AttachmentManager.streamAttachmentContent(Attachment attachment, InputStreamConsumer<T> consumer)
which is available since 6.1. Unfortunately, I haven't found a usage example and not sure how to construct an InputStreamConsumer. Would appreciate an Atlassian dev jumping in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Community moderators have prevented the ability to post new answers.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.