PDFbox с Restlet


У меня возникла проблема с обслуживанием PDF-файла из нашего Restlet API.

Я использую базовый пример кода издокументации Apache PDFBox , он прекрасно работает вне контекста Restlet.

PDDocument document = new PDDocument();

System.err.println("before instantiating new PDPage");
// Create a new blank page and add it to the document
PDPage page = new PDPage(); // LINE FAILING IN RESTLET
System.err.println("after instantiating new PDPage");

document.addPage(page);
document.save("pdf.pdf");
document.close();

Вот моя попытка использовать PDFBox в ресурсе, в конечном итоге я хочу вернуть OutputRepresentation и сохранить PDDcoument в поток.

Следующий код перестает работать на PDPage page = new PDPage();, я не получаю никаких исключений, сервер Restlet не возвращает никакого ответа. Текст "after instantiating new PDPage" никогда не печатается.

EDIT: я упростил свой код, насколько это возможно, но у меня все еще есть проблема

Вот мой основной маршрутизатор:

public class ApiRestletApplication extends Application {

  @Override
  public Restlet createInboundRoot() {
    Router router = new Router(getContext());
    router.attach("/v1/myresource", MyResource.class);
    return router;
  }
}

Вот мой ресурс

public class MyResource extends ServerResource {

  protected static final Logger logger = LoggerFactory.getLogger(MyResource.class);

  @Get
  public Representation toPDF() {

    PDDocument document = new PDDocument();
    System.err.println("before instantiating new PDPage");
    PDPage page = new PDPage();
    System.err.println("after instantiating new PDPage"); //<= never printed
    document.addPage(page);

    return new PDFRepresentation(document);
  }
}

Вот моя паутина.xml

<!-- Restlet application -->
<context-param>
  <param-name>org.restlet.application</param-name>
  <param-value>com.xxx.api.ApiRestletApplication</param-value>
</context-param>

<!-- Restlet adapter -->
<servlet>
  <servlet-name>RestletServlet</servlet-name>
  <servlet-class>
    org.restlet.ext.servlet.ServerServlet
  </servlet-class>
</servlet>

<!-- Catch all requests -->
<servlet-mapping>
  <servlet-name>RestletServlet</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

Если я закомментирую строку с объявлением PDPage и верну некоторые StringRepresentation, все работает нормально. Я могу обслуживать некоторые Json, xml и excel. Но этот PDF сводит меня с ума.

Вот версия, которую я использую:

[INFO] +- org.restlet.jee:org.restlet:jar:2.2.1:compile
[INFO] +- org.restlet.jee:org.restlet.ext.crypto:jar:2.2.1:compile
[INFO] +- org.restlet.jee:org.restlet.ext.servlet:jar:2.2.1:compile

[INFO] |  +- org.apache.pdfbox:pdfbox:jar:1.8.10:compile
[INFO] |  |  +- org.apache.pdfbox:fontbox:jar:1.8.10:compile
[INFO] |  |  - org.apache.pdfbox:jempbox:jar:1.8.10:compile
[INFO] |  - com.sun:tools:jar:jdk:system

Здесь является ли запрос завитка:

curl "http://localhost:8889/v1/myresource" -H "Content-Type: application/pdf" -H "Accept: application/pdf"

Вот журналы в eclipse:

2015-09-21 15:08:15.933:INFO::Started SelectChannelConnector@0.0.0.0:8889
2015-09-21 15:08:20.698:INFO:/:RestletServlet: [Restlet] Attaching application: com.xxx.api.ApiRestletApplication@2613622c to URI: 
before instantiating new PDPage
//then nothing
Спасибо за вашу помощь.

Правка 2: следующий код работает, но я до сих пор не знаю, почему мой код не работает:

public class RestletServerTest extends Application {

  @Override
  public Restlet createInboundRoot() {
    Router router = new Router(getContext());
    router.attach("/v1/myresource", MyResource.class);
    return router;
  }

  public static void main(String[] args) throws Exception {
    Component component = new Component();
    component.getServers().add(Protocol.HTTP, 8182);

    component.getDefaultHost().attach("", new RestletServerTest());
    component.start();
  }
}

Правка 3: проблема, похоже, не связана с Restlet, но с PDFBox и сервлетами: PDFBox: не удается сохранить pdf во время работы на tomcat

Правка 4: вот решение https://stackoverflow.com/a/32706385/1039265

3 2

3 ответа:

Я попробовал ваш пример кода, и он сработал для меня.

Я только что настроил класс PDDocumentRepresentation, который обертывает PDDocument:

import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.restlet.data.MediaType;
import org.restlet.representation.OutputRepresentation;

public class PDDocumentRepresentation extends OutputRepresentation {

    private PDDocument document = new PDDocument();

    public PDDocumentRepresentation(PDDocument document) {
        super(MediaType.APPLICATION_PDF);
        this.document = document;
    }

    @Override
    public void write(OutputStream outputStream) throws IOException {
        try {
            document.save(outputStream);
            document.close();
        } catch (COSVisitorException e) {
            throw new IOException(e);
        }
    }
}

Вот код ресурса:

public class MyResource extends ServerResource {

    @Get
    public Representation getPdf() {
        PDDocument document = new PDDocument();
        PDPage page = new PDPage();
        document.addPage(page);

        return new PDDocumentRepresentation(document);
    }
}

Фактически вы никогда не возвращаете содержимое вашего созданного PDF-файла в ответ. Возвращаемое представление аннотированного метода соответствует содержимому ответа.

В вашем случае он должен соответствовать двоичному. Вы также можете использовать заголовок Content disposition для запуска диалогового окна загрузки в браузере.

Вы можете попытаться поместить это содержимое в массив байтов, а затем отправить его через Restlet с выделенным представлением (например, на основе OuputStream).

Надеюсь, это вам поможет. Тьерри

Проблема исходит из версии PDFBox. Если я использую версию 2.0.0-SNAPSHOT (http://pdfbox.apache.org/2.0/getting-started.html ), я могу генерировать PDF через сервлет. Спасибо за вашу помощь.