Рендеринг 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.