Рендеринг PDF-файла и сохранение на объекте с помощью плагинов Grails Rendering и Attachmentable

Я пытаюсь создать файл PDF, содержащий информацию об объекте, а затем присоединить его к другому объекту, который хранится в базе данных. Плагин прикрепляемых файлов, который я использую, теперь работает для пользовательских вложений, но мне нужна моя система, чтобы иметь возможность делать это автоматически.

Я использую:
Граальс 1.3.9
Attachmentable 0.3.0 http://grails.org/plugin/attachmentable
Рендеринг 0.4.3 http://grails.org/plugin/rendering

Я был в состоянии генерировать и отображать PDF, но не знаю, как его прикрепить с помощью плагина attachmentable. Мне нужен какой-то способ взять сгенерированный байтовый массив pdf и преобразовать его в MultipartFile для функции подключаемого модуля, которую я вызываю. Полученная ошибка показывает, что мои типы аргументов недопустимы.

Я сохраняю object1 и object2, затем генерирую pdf для object1 и пытаюсь прикрепить его к object2.

Заранее спасибо за вашу помощь!

Фрагменты контроллера Thing1:

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

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

Функция AttachmentableService, которую я пытаюсь вызвать:

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:

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)

Метод обслуживания, который я добавил:

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
}

1 ответ

Решение

Похоже, что AttachmentableService, на который вы ссылались (из плагина Attachmentable), предполагает, что он имеет дело со сценарием загрузки файла, так что вы можете легко получить экземпляр MultipartFile через request.getFile(). Это не так - вы создаете файл с помощью плагина рендеринга и хотите, чтобы этот файл был присоединен к объекту домена, верно?

Вы можете попробовать создать экземпляр CommonsMultipartFile вручную, сначала записав pdf-байты на диск, а затем создать DiskFileItem через DiskFileItemFactory. Посмотрите этот пост для примера того, что я думаю: Как сделать CommonsMultipartFile из абсолютного пути к файлу?

Другой, лучше, вариант может заключаться в извлечении источника этого плагина и добавлении метода, который не требует, чтобы вы проходили эти движения, - возможно, версии метода addAttachment, который вместо этого принимает File или OutputStream - и передает PR в Автор плагина. (Похоже, они добавляют метод addAttachment к соответствующим объектам домена, который также ожидает CommonsMultipartFile).

В противном случае вам, возможно, придется просто создать собственную службу, чтобы в основном обеспечить тот же конечный результат, который, очевидно, заключается в создании AttachmentLink и связанного экземпляра Attachment.

Другие вопросы по тегам