使用jax-rs从REST服务下载xml,而无需在本地存储文件 [英] Download xml from REST service using jax-rs without locally storing the file

查看:114
本文介绍了使用jax-rs从REST服务下载xml,而无需在本地存储文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In a service, I am creating an XML document named doc and I want the user to receive a prompt to download the document without having to save it locally (like the one that says open or save the file).

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

However, I am not able to find how I should build the response that is going to be returned, or even what type to @produce.

到目前为止,我有这个:

So far I have this:

@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();
    }

}

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

My main problem is that I cannot find how to build the response that is going to be returned. The answers that I have found download an already existing file that is locally stored.

最接近答案的线程是:

The thread that is closest on answering is: How to make an XML document downloadable without intermediate file storage?

但是我不明白如何将其应用于与HttpServletResponse不同的REST服务响应上.

but I cannot understand how to apply it on my REST service response which differs from the HttpServletResponse.

推荐答案

如果查看链接到的答案,则会看到

If you look at the answer you linked to, you will see that a StreamResult is used. In the answer, a StringWriter is passed to the constructor, but if you look at the Javadoc, it has an overloaded constructor that also takes an OutputStream. So if you're returning a StreamingOutput, just pass the OutputStream from the StreamingOutput#write(OutputStream) method to the StreamResult constructor. Everything else from the answer should be the same.

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).如果数据是XML 1 ,则将application/octet-stream设置为没有意义.

Here's the complete resource class I used to test. Notice that I use @Produces(MediaType.APPLICATION_XML). There's no point in setting to application/octet-stream if the data is XML1.

@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结果),我们实际上需要在Content-Disposition标头中添加attachment值.为此,我们应该返回Response,而不是从方法中返回StreamingOutput,其中实体将为StreamingOutput

Update

To automatically download the file (as opposed to displaying the XML result), we actually need to add the Content-Disposition header with the attachment value. To do that, instead of returning StreamingOutput from the method, we should return Response, where the entity will be the 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结构.已经有实体提供程序可以处理从POJO进行的转换到我们的XML.例如,如果我们具有以下POJO(需要用@XmlRootElement注释)

Update 2

If you don't already know, you can simply return your POJOs (or list of them) and they will automatically get serialized to XML. You don't need to manually use the DOM classes to create XML structure. There are already Entity Providers that will handle the conversion from POJO to XML for us. For example, if we have the following POJO (needs to be annotated with @XmlRootElement)

@XmlRootElement
public class User {
    private String name;

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

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

然后我们可以将其返回,它将自动序列化为

then we can just return it and it will automatically be serialized to

<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

It's a lot less messy isn't it? If you want to return a list of Users, then you need to wrap it in GenericEntity

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


1.请参阅:我是否需要Content-Type:application/octet-stream进行文件下载?


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

这篇关于使用jax-rs从REST服务下载xml,而无需在本地存储文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆