简体   繁体   中英

Render a PDF file and save to object using grails Rendering and Attachmentable plugins

I am attempting to generate a PDF file that contains object information and then attach it to another object that is stored in the database. The attachmentable plugin I am using is working now for user end attachments, but I need my system to be able to do it automatically.

I am using:
Grails 1.3.9
Attachmentable 0.3.0 http://grails.org/plugin/attachmentable
Rendering 0.4.3 http://grails.org/plugin/rendering

I have been able to generate and display the pdf, but do not know how to attach it using the attachmentable plugin. I need some way to take the generated pdf byte array and convert it to a MultipartFile for the attachmentable plugin function I call. The error I get shows that my argument types are invalid.

I save object1 and object2, then generate the pdf of object1 and try to attach it to object2.

Thanks in advance for you help!

Thing1 Controller Snippets:

ByteArrayOutputStream bytes = pdfRenderingService.render(template: "/thing1/pdf", model: [thing1: thing1])

attachmentableService.addAttachment("unknown", thing2.id, bytes)

AttachmentableService function I am attempting to call:

def addAttachment(def poster, def reference, CommonsMultipartFile file) {
    addAttachment(CH.config, poster, reference, file)
}

def addAttachment(def config,
                  def poster,
                  def reference,
                  CommonsMultipartFile file) {

    if (reference.ident() == null) {
        throw new AttachmentableException(
            "You must save the entity [${delegate}] before calling addAttachment.")
    }

    if (!file?.size) {
        throw new EmptyFileException(file.name, file.originalFilename)
    }

    String delegateClassName = AttachmentableUtil.fixClassName(reference.class)
    String posterClass = (poster instanceof String) ? poster : AttachmentableUtil.fixClassName(poster.class.name)
    Long posterId = (poster instanceof String) ? 0L : poster.id
    String filename = file.originalFilename

    // link
    def link = AttachmentLink.findByReferenceClassAndReferenceId(
            delegateClassName, reference.ident())
    if (!link) {
        link = new AttachmentLink(
                referenceClass: delegateClassName,
                referenceId: reference.ident())
    }

    // attachment
    Attachment attachment = new Attachment(
            // file
            name: FilenameUtils.getBaseName(filename),
            ext: FilenameUtils.getExtension(filename),
            length: 0L,
            contentType: file.contentType,
            // poster
            posterClass: posterClass,
            posterId: posterId,
            // input
            inputName: file.name)
    link.addToAttachments attachment

    if (!link.save(flush: true)) {
        throw new AttachmentableException(
                "Cannot create Attachment for arguments [$user, $file], they are invalid.")
    }

    // save file to disk
    File diskFile = AttachmentableUtil.getFile(config, attachment, true)
    file.transferTo(diskFile)

    attachment.length = diskFile.length()

    // interceptors
    if(reference.respondsTo('onAddAttachment')) {
        reference.onAddAttachment(attachment)
    }

    attachment.save(flush:true) // Force update so searchable can try to index it again.

    return reference
}

Grails runtime error:

groovy.lang.MissingMethodException: No signature of method: com.macrobit.grails.plugins.attachmentable.services.AttachmentableService.addAttachment() is applicable for argument types: (java.lang.String, java.lang.Long, java.io.ByteArrayOutputStream) values: [unknown, 80536, %PDF-1.4 and a long string of unreadable data...]
Possible solutions: addAttachment(java.lang.Object, java.lang.Object, org.springframework.web.multipart.commons.CommonsMultipartFile), addAttachment(java.lang.Object, java.lang.Object, java.lang.Object, org.springframework.web.multipart.commons.CommonsMultipartFile)

Service Method I Added:

def customAddMethod(def poster, def reference, def pdfBytes) {
    customAddMethod(CH.config, poster, reference, pdfBytes)
}

def customAddMethod(def config,
                  def poster,
                  def reference,
                  def pdfBytes) {

    if (reference.ident() == null) {
        throw new AttachmentableException(
            "You must save the entity [${delegate}] before calling customAddMethod.")
    }

    String delegateClassName = AttachmentableUtil.fixClassName(reference.class)
    String posterClass = (poster instanceof String) ? poster : AttachmentableUtil.fixClassName(poster.class.name)
    Long posterId = (poster instanceof String) ? 0L : poster.id
    String filename = "File Name"

    // link
    def link = AttachmentLink.findByReferenceClassAndReferenceId(
            delegateClassName, reference.ident())

    if (!link) {
        link = new AttachmentLink(
                referenceClass: delegateClassName,
                referenceId: reference.ident())
    }

    // attachment
    Attachment attachment = new Attachment(
            // file
            name: "File Name",
            ext: "pdf",
            length: 0L,
            contentType: "application/pdf",
            // poster
            posterClass: posterClass,
            posterId: posterId,
            // input
            inputName: "File Name")
    link.addToAttachments attachment

    if (!link.save(flush: true)) {
        throw new AttachmentableException(
                "Cannot create Attachment for arguments [$user, $file], they are invalid.")
    }

    // save file to disk
    byte[] bytes = pdfBytes.toByteArray(); //convert ByteArrayOutputStream to ByteArray

    File diskFile = AttachmentableUtil.getFile(config, attachment, true) //file path
    FileOutputStream fos = new FileOutputStream(diskFile); //open file output stream to write to
    fos.write(bytes); //write rendered pdf bytes to file
    fos.flush();
    fos.close();

    attachment.length = diskFile.length()

    // interceptors
    if(reference.respondsTo('onAddAttachment')) {
        reference.onAddAttachment(attachment)
    }

    attachment.save(flush:true) // Force update so searchable can try to index it again.

    return reference
}

It looks like the AttachmentableService you referenced (from the Attachmentable plugin) assumes it's dealing with a file-upload scenario, such that you could easily grab the MultipartFile instance via request.getFile(). That's not the case for you - you're creating the file via the Rendering plugin, and you want that file attached to a domain object, right?

You could try constructing a CommonsMultipartFile instance manually by first writing the pdf bytes to disk, and then create a DiskFileItem via DiskFileItemFactory. See this post for an example of what I'm thinking: How to make CommonsMultipartFile from absolute file path?

Another, better, option might be to checkout that plugin's source and add a method that doesn't require you to go through those gyrations - perhaps a version of the addAttachment method that accepts a File or an OutputStream instead - and submit a PR to the plugin author. (Looks like they're adding an 'addAttachment' method to qualifying domain objects, which also expects a CommonsMultipartFile).

Otherwise, you might just have to create your own service to basically provide the same end result, which apparently is to create an AttachmentLink and associated Attachment instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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