使用 jax-rs 从 REST 服务下载 xml 而无需本地存储文件
Download xml from REST service using jax-rs without locally storing the file
在一项服务中,我正在创建一个名为 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?
但我不明白如何将它应用于我的 REST 服务响应,这与 HttpServletResponse 不同。
如果您查看链接到的答案,您会看到使用了 StreamResult
。在答案中,一个 StringWriter
被传递给构造函数,但是如果你查看 Javadoc,它有一个重载的构造函数,它也接受一个 OutputStream
。因此,如果您 returning a StreamingOutput
,只需将 StreamingOutput#write(OutputStream)
方法中的 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();
}
}
};
这是我用来测试的完整资源class。请注意,我使用 @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 结果),我们实际上需要添加 Content-Disposition
header 和 attachment
值。为此,我们应该 return Response
,而不是从方法中 returning 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
如果您还不知道,您可以简单地 return 您的 POJO(或它们的列表),它们将自动序列化为 XML。您不需要手动使用 DOM classes 来创建 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;
}
}
那么我们就可以return它,它会自动序列化为
<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();
}
}
不那么乱了,不是吗?如果你想 return 用户列表,那么你需要将它包装在 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?
在一项服务中,我正在创建一个名为 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?
但我不明白如何将它应用于我的 REST 服务响应,这与 HttpServletResponse 不同。
如果您查看链接到的答案,您会看到使用了 StreamResult
。在答案中,一个 StringWriter
被传递给构造函数,但是如果你查看 Javadoc,它有一个重载的构造函数,它也接受一个 OutputStream
。因此,如果您 returning a StreamingOutput
,只需将 StreamingOutput#write(OutputStream)
方法中的 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();
}
}
};
这是我用来测试的完整资源class。请注意,我使用 @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 结果),我们实际上需要添加 Content-Disposition
header 和 attachment
值。为此,我们应该 return Response
,而不是从方法中 returning 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
如果您还不知道,您可以简单地 return 您的 POJO(或它们的列表),它们将自动序列化为 XML。您不需要手动使用 DOM classes 来创建 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;
}
}
那么我们就可以return它,它会自动序列化为
<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();
}
}
不那么乱了,不是吗?如果你想 return 用户列表,那么你需要将它包装在 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?