【问题标题】:Download xml from REST service using jax-rs without locally storing the file使用 jax-rs 从 REST 服务下载 xml,而不在本地存储文件
【发布时间】:2018-08-10 16:55:42
【问题描述】:

在一项服务中,我正在创建一个名为 doc 的 XML 文档,我希望用户收到下载该文档的提示,而无需将其保存在本地(例如打开或保存文件的那个)。

但是,我无法找到应该如何构建将要返回的响应,甚至无法找到 @produce 的类型。

到目前为止,我有这个:

@GET
@Path("/getXML")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public StreamingOutput getXML(
        @FormParam("id") int id) {
    UserDB userDao = new UserDB();
    entities.User userd = userDao.getById(id);

    DocumentBuilderFactory icFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder icBuilder;

    try {
        icBuilder = icFactory.newDocumentBuilder();
        Document doc = icBuilder.newDocument();

        Element rootElement = doc.createElement("Users");
        doc.appendChild(rootElement);

        rootElement.appendChild(getUser(doc, "1", "asd", "adas"));
        rootElement.appendChild(getUser(doc, "2", "bbb", "ccc"));

        //Here I should return the doc that is going to be downloaded
    }
    catch (Exception e) {
        e.printStackTrace();
    }

}

EDIT1:我的主要问题是我找不到如何构建将要返回的响应。我找到的答案下载了本地存储的现有文件。

最接近回答的主题是:How to make an XML document downloadable without intermediate file storage?

但我不明白如何将它应用于与 HttpServletResponse 不同的 REST 服务响应。

【问题讨论】:

  • 见我的latest update。我有一种感觉,这可能就是你想要做的。

标签: java xml rest download jax-rs


【解决方案1】:

如果您查看链接到的答案,您会看到使用了StreamResult。在答案中,StringWriter 被传递给构造函数,但是如果你查看 Javadoc,它有一个重载的构造函数,它也接受一个 OutputStream。因此,如果您要返回StreamingOutput,只需将OutputStream 从StreamingOutput#write(OutputStream) 方法传递给StreamResult 构造函数。答案中的其他所有内容都应该相同。

return new StreamingOutput() {
    @Override
    public void write(OutputStream out)
            throws IOException, WebApplicationException {
        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            StreamResult result = new StreamResult(out);
            DOMSource source = new DOMSource(doc);
            transformer.transform(source, result);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
};

这是我用来测试的完整资源类。请注意,我使用@Produces(MediaType.APPLICATION_XML)。如果数据是 XML1,则设置为 application/octet-stream 没有意义。

@Path("dom")
public class DomXmlResource {

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public StreamingOutput getXml() throws Exception {

        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

        Document doc = docBuilder.newDocument();
        Element rootElement = doc.createElement("company");
        doc.appendChild(rootElement);

        Element staff = doc.createElement("Staff");
        rootElement.appendChild(staff);

        staff.setAttribute("id", "1");

        Element firstname = doc.createElement("firstname");
        firstname.appendChild(doc.createTextNode("yong"));
        staff.appendChild(firstname);

        return new StreamingOutput() {
            @Override
            public void write(OutputStream out)
                    throws IOException, WebApplicationException {
                try {
                    Transformer transformer = TransformerFactory.newInstance()
                            .newTransformer();
                    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                    StreamResult result = new StreamResult(out);
                    DOMSource source = new DOMSource(doc);
                    transformer.transform(source, result);
                    out.flush();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
    }
}

更新

要自动下载文件(而不是显示 XML 结果),我们实际上需要添加带有 attachment 值的 Content-Disposition 标头。为此,我们应该返回Response,而不是从方法中返回StreamingOutput,其中实体将是StreamingOutput

@Path("dom")
public class DomXmlResource {

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public Response getXml() throws Exception {
        ...
        StreamingOutput entity = new StreamingOutput() {
            @Override
            public void write(OutputStream out)
                    throws IOException, WebApplicationException {
                ...
            }
        };
        return Response.ok(entity)
                .header(HttpHeaders.CONTENT_DISPOSITION, 
                        "attachment;filename=file.xml")
                .build();
    }
}

更新 2

如果您还不知道,您可以简单地返回您的 POJO(或它们的列表),它们会自动序列化为 XML。您不需要手动使用 DOM 类来创建 XML 结构。已经有Entity Providers 为我们处理从 POJO 到 XML 的转换。比如我们有如下POJO(需要用@XmlRootElement注解)

@XmlRootElement
public class User {
    private String name;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

然后我们可以直接返回它,它会自动序列化为

<user><name>footer</name></user>

这是一个例子

@Path("pojo")
public class PojoXmlResource {

    @GET
    @Produces("application/xml")
    public Response getXml() {
        User user = new User();
        user.setName("Jane Doe");

        return Response.ok(user)
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment;filename=user.xml")
                .build();
    }
}

它不那么凌乱了,不是吗?如果要返回用户列表,则需要将其包装在GenericEntity中

List<User> users = Arrays.asList(user1, user2, user3);
GenericEntity<List<User>> entity = new GenericEntity<List<User>>(users){};
return Response.ok(entity)
        ...
        .build();

1。见:Do I need Content-Type: application/octet-stream for file download?

【讨论】:

    猜你喜欢
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    相关资源
    最近更新 更多