在一项服务中,我正在创建一个名为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服务响应上。
答案 0 :(得分:2)
如果查看链接到的答案,则会看到使用了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)
。如果数据是XML 1 ,则设置为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结果相反),我们实际上需要在Content-Disposition
头中添加attachment
值。为此,我们应该返回StreamingOutput
,而不是从方法中返回Response
,其中实体将是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();
}
}
如果您还不知道,则只需返回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?