Java 文件与restful web服务中的其他对象一起上载
我想通过上传一张图片和员工数据在系统中创建一个员工信息。我可以使用jersey进行不同的rest通话。但我想在一次休息通话中实现这一目标。 我提供以下结构。请帮我在这方面怎么做Java 文件与restful web服务中的其他对象一起上载,java,jersey,jax-rs,multipartform-data,postman,Java,Jersey,Jax Rs,Multipartform Data,Postman,我想通过上传一张图片和员工数据在系统中创建一个员工信息。我可以使用jersey进行不同的rest通话。但我想在一次休息通话中实现这一目标。 我提供以下结构。请帮我在这方面怎么做 @POST @Path("/upload2") @Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response uploadFileWithData(
@POST
@Path("/upload2")
@Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response uploadFileWithData(
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
Employee emp) {
//..... business login
}
每当我尝试这样做时,我都会在Chrome postman中出错。下面给出了我的雇员json的简单结构
{
"Name": "John",
"Age": 23,
"Email": "john@gmail.com",
"Adrs": {
"DoorNo": "12-A",
"Street": "Street-11",
"City": "Bangalore",
"Country": "Karnataka"
}
}
但是,我可以通过两个不同的调用来实现,但我希望在一个rest调用中实现,这样我就可以接收文件以及员工的实际数据
请您在这方面提供帮助。您不能有两个
内容类型(从技术上讲,这就是我们下面要做的,但它们与多部分的每个部分分开,但主要类型是多部分)。这基本上就是你对你的方法的期望。您希望mutlipart和json一起作为主要媒体类型。Employee
数据需要是多部分的一部分。因此,您可以为员工添加@FormDataParam(“emp”)
@POST
@Path("/upload2")
@Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response uploadFileWithData(
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
Employee emp) {
//..... business login
}
@FormDataParam("emp") Employee emp) { ...
这是我用来测试的类
@Path("/multipart")
public class MultipartResource {
@POST
@Path("/upload2")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadFileWithData(
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition cdh,
@FormDataParam("emp") Employee emp) throws Exception{
Image img = ImageIO.read(fileInputStream);
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
System.out.println(cdh.getName());
System.out.println(emp);
return Response.ok("Cool Tools!").build();
}
}
首先,我刚刚测试了客户端API,以确保它能正常工作
@Test
public void testGetIt() throws Exception {
final Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class)
.build();
WebTarget t = client.target(Main.BASE_URI).path("multipart").path("upload2");
FileDataBodyPart filePart = new FileDataBodyPart("file",
new File("stackoverflow.png"));
// UPDATE: just tested again, and the below code is not needed.
// It's redundant. Using the FileDataBodyPart already sets the
// Content-Disposition information
filePart.setContentDisposition(
FormDataContentDisposition.name("file")
.fileName("stackoverflow.png").build());
String empPartJson
= "{"
+ " \"id\": 1234,"
+ " \"name\": \"Peeskillet\""
+ "}";
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
Response response = t.request().post(
Entity.entity(multipartEntity, multipartEntity.getMediaType()));
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
response.close();
}
我刚刚创建了一个简单的Employee
类,其中有一个id
和name
字段用于测试。这个很好用。它显示图像,打印内容配置,并打印Employee
对象
我对邮递员不太熟悉,所以我把那个测试留到最后:-)
它似乎也可以正常工作,正如您看到的响应“酷工具”
。但是如果我们查看打印的Employee
数据,就会发现它是空的。这很奇怪,因为使用客户端API它工作得很好
如果我们查看预览窗口,就会看到问题所在
emp
主体部分没有内容类型
标题。您可以在客户端API中看到我显式地设置了它
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
所以我想这只是完整答案的一部分。就像我说的,我不熟悉邮递员,所以我不知道如何为身体的各个部位设置内容类型。图像的image/png
是为我自动为图像部分设置的(我想它只是由文件扩展名决定的)。如果你能解决这个问题,那么问题就应该解决了。如果你知道怎么做,请把它作为答案贴出来
请参阅下面的更新以了解解决方案
为了完整起见。。。
基本配置:
依赖关系:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey2.version}</version>
</dependency>
服务器配置:
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.multipart")
.register(MultiPartFeature.class);
如果您在服务器配置方面遇到问题,以下文章之一可能会有所帮助
更新
因此,正如您从Postman客户端看到的,一些客户端无法设置单个部件的内容类型,这包括浏览器在使用FormData
(js)时的默认功能
我们不能期望客户机发现这一点,所以我们可以做的是,在接收数据时,在反序列化之前显式设置内容类型。比如说
@POST
@Path("upload2")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@FormDataParam("emp") FormDataBodyPart jsonPart,
@FormDataParam("file") FormDataBodyPart bodyPart) {
jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
Employee emp = jsonPart.getValueAs(Employee.class);
}
获得POJO需要做一些额外的工作,但这是一个比强迫客户机尝试找到自己的解决方案更好的解决方案
另一个选项是使用字符串参数,并使用任何JSON库将字符串反序列化到POJO(如Jackson ObjectMapper)。对于前面的选项,我们只让Jersey处理反序列化,它将使用与所有其他JSON端点相同的JSON库(这可能是首选)
旁白
- 如果您使用的连接器与默认的HttpUrlConnection不同,您可能会对其中的一个对话感兴趣
您可以使用以下代码使用多部分表单数据从表单访问图像文件和数据
您的应用程序配置应该从glassfish.jersey.media.注册MultiPartFeature.class。。以便能够上传文件
@javax.ws.rs.ApplicationPath(ResourcePath.API_ROOT)
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
//register the necessary headers files needed from client
register(CORSConfigurationFilter.class);
//The jackson feature and provider is used for object serialization
//between client and server objects in to a json
register(JacksonFeature.class);
register(JacksonProvider.class);
//Glassfish multipart file uploader feature
register(MultiPartFeature.class);
//inject and registered all resources class using the package
//not to be tempered with
packages("com.flexisaf.safhrms.client.resources");
register(RESTRequestFilter.class);
}
我使用了来自的文件上载示例
在我的资源类中,我有以下方法
@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response attachupload(@FormDataParam("file") byte[] is,
@FormDataParam("file") FormDataContentDisposition fileDetail,
@FormDataParam("fileName") String flename){
attachService.saveAttachment(flename,is);
}
public void saveAttachment(String flename, byte[] is) {
// TODO Auto-generated method stub
attachmentDao.saveAttachment(flename,is);
}
在我的attachService.java中,我有以下方法
@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response attachupload(@FormDataParam("file") byte[] is,
@FormDataParam("file") FormDataContentDisposition fileDetail,
@FormDataParam("fileName") String flename){
attachService.saveAttachment(flename,is);
}
public void saveAttachment(String flename, byte[] is) {
// TODO Auto-generated method stub
attachmentDao.saveAttachment(flename,is);
}
在岛上我有
attach.setData(is);
attach.setFileName(flename);
在我的HBM映射中
<property name="data" type="binary" >
<column name="data" />
</property>
这适用于所有类型的文件,如.PDF、.TXT、.PNG等。当我尝试使用Jersey client 2.21.1时,出现了400个错误。当我在客户端代码中添加以下内容时,它起到了作用:
MediaType contentType = MediaType.MULTIPART_FORM_DATA_TYPE;
contentType = Boundary.addBoundary(contentType);
Response response = t.request()
.post(Entity.entity(multipartEntity, contentType));
而不是在POST请求调用中硬编码的MediaType.MULTIPART\u FORM\u DATA
之所以需要这样做,是因为当您为Jersey客户端使用不同的连接器(如Apache)时,它无法更改出站头,这是向内容类型添加边界所必需的。此限制在中进行了解释。因此,如果您想使用不同的连接器,则需要手动创建边界。请求类型是多部分/表单数据,您发送的基本上是表单字段,这些字段以字节形式输出,内容边界分隔不同的表单字段。要将对象表示形式作为表单字段(字符串)发送,您可以从客户端发送序列化表单,然后在服务器上反序列化
毕竟,没有任何编程环境对象真正在网络上运行。两边的编程环境都在进行自动序列化和反序列化,您也可以这样做。这是最干净和编程环境怪癖自由的方式来做这件事
例如,这里有一个javascript客户端发布到Jersey示例服务
submitFile(){
let data = new FormData();
let account = {
"name": "test account",
"location": "Bangalore"
}
data.append('file', this.file);
data.append("accountKey", "44c85e59-afed-4fb2-884d-b3d85b051c44");
data.append("device", "test001");
data.append("account", JSON.stringify(account));
let url = "http://localhost:9090/sensordb/test/file/multipart/upload";
let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
axios.post(url, data, config).then(function(data){
console.log('SUCCESS!!');
console.log(data.data);
}).catch(function(){
console.log('FAILURE!!');
});
},
在这里,客户端正在发送一个文件、两个表单字段(字符串)和一个帐户对象,该对象已被传输字符串化。下面是表单字段在导线上的外观
在服务器上,您可以按照自己认为合适的方式反序列化表单字段。为了完成这个小例子
@POST
@Path("/file/multipart/upload")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadMultiPart(@Context ContainerRequestContext requestContext,
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition cdh,
@FormDataParam("accountKey") String accountKey,
@FormDataParam("account") String json) {
System.out.println(cdh.getFileName());
System.out.println(cdh.getName());
System.out.println(accountKey);
try {
Account account = Account.deserialize(json);
System.out.println(account.getLocation());
System.out.println(account.getName());
} catch (Exception e) {
e.printStackTrace();
}
return Response.ok().build();
}
嗨,我可以麻烦你(或其他任何人)吗