美文网首页Java技术升华jerseyRESTful webservice
Jersey 开发RESTful(十二)Jersey上传下载

Jersey 开发RESTful(十二)Jersey上传下载

作者: 叩丁狼教育 | 来源:发表于2018-01-31 12:38 被阅读212次

    【原创文章,转载请注明原文章地址,谢谢!】

    在本节中,我们将简单介绍Jersey中的上传下载,和Response的使用。

    上传

    在Jersey中的上传相对来说还是比较简单的。首先要了解的是,Jersey针对multipart需要专门的支持,首先在maven中添加multipart的依赖:

        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-multipart</artifactId>
            <version>2.25</version>
        </dependency>
    

    接着,需要在Jersey中引入MultipartFeature。MultiPartFeature是Jersey中针对Multipart的一种特征(Feature,Feature是JAX-RS中的一种规范,可以视为一种特殊的meta-provider,通常把多个相同类型的Provider封装到一起,比如针对多种encoding的Provider打包注册);
    在Jersey中有几种注册MultipartFeature的方式:
    1,在web.xml中注册:只需要在Jersey的ServletContainer中添加initparam即可:

    <init-param>
            <param-name>jersey.config.server.provider.classnames</param-name>
            <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
        </init-param>
    

    2,如果是使用Servlet3.0的方式启动,需要在ResourceConfig中添加MultipartFeature:

    @ApplicationPath("webapi")
    public class RestApplication extends ResourceConfig {
        public RestApplication() {
            this.packages("cn.wolfcode.jersey");
            this.register(MultiPartFeature.class);
        }
    }
    

    配置完成之后,只需要完成对应的资源类即可:

    @POST
    @Path("image1")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public String upload(@FormDataParam("file") InputStream fileInputStream,
            @FormDataParam("file") FormDataContentDisposition disposition, @Context ServletContext ctx) {
        
        return "success";
    }
    

    简单解释一下这段代码:
    1,请求必须是POST的,这个不用多说;
    2,请求返回json格式响应;
    3,Consumes这次设置的是MediaType.MULTIPART_FORM_DATA,这个很重要,因为要能够上传,需要要求表单的提交格式为multipart/form-data;
    4,重点在于资源方法的参数,在这里,我们使用了@FormDataParam(该标签来源于jersey的multipart包),该标签能够在资源方法上绑定请求编码类型为multipart/form-data中的每一个实体项。该标签可以绑定如下一些类型:

    • FormDataBodyPart;
    • List<FormDataBodyPart>;
    • FormDataContentDisposition;
    • List<FormDataContentDisposition>;
    • InputStream;

    在介绍重点对象之前,我们先来简单回顾一下上传的基本概念。我们知道上传的时候,不能使用默认的x-www-form-urlencoded方式,而要选择使用multipart/form-data二进制传输方式,文件的内容才能够正常提交。那么使用Multipart/form-data方式提交,在请求实体中展示的内容如下:

    image.png

    其中,每一段内容(key:name,value:ssss和key:file,value:文件内容)就会被包装到一段content-disposition里面,用于代表本次提交的请求参数和请求文件内容;

    明确请求格式之后,我们重点来关注三个对象:
    1,FormDataBodyPart:对使用multipart/form-data编码请求实体的中的每一段实体内容的封装;提供了一些常见的方法:

    //获取某一段实体请求的名字;
    System.out.println(bp.getName());
    //获取某一段实体请求的值(该方法只能作用于text/plain内容)
    System.out.println(bp.getValue());
    //获取某一段实体请求的封装对象,得到一个BodyPartEntity对象,可以调用getInputStream()方法获取实体内容等;
    System.out.println(bp.getEntity());
    //可以将请求转化为输入流(这也是一种获取上传文件内容的方法);
    InputStream is=bp.getValueAs(InputStream.class);
    //包装成FormDataContentDisposition对象返回;
    FormDataContentDisposition disp=bp.getFormDataContentDisposition();
    

    2,FormDataContentDisposition:对multipart/form-data中一段content-disposition的封装,较之FormDataBodyPart提供了更高级的方法抽象:

    //获取文件名;
    System.out.println("getFileName : " + disposition.getFileName());
    //获取字段名称,即<input type="file" name="xxx")中的xxx
    System.out.println("getName : " + disposition.getName());
    //获取该段content-disposition的内容长度
    System.out.println("getSize : " + disposition.getSize());
    //获取该段content-disposition的类型,比如form-data
    System.out.println("getType : " + disposition.getType());
    //获取本次请求的请求值,比如{name=file, filename=3.jpg}
    System.out.println("getParameters : " + disposition.getParameters());
    

    3,InputStream:直接将提交的内容包装为输入流,这是最简单的上传文件方法;

    介绍完几个重点对象,我们来看一个完整的上传文件的例子,使用两种方式完成:

    @POST
    @Path("image1")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public String upload(@FormDataParam("file") InputStream fileInputStream,
            @FormDataParam("file") FormDataContentDisposition disposition, @Context ServletContext ctx) {
    
        File upload = new File(ctx.getRealPath("/upload"),
                UUID.randomUUID().toString() + "." + FilenameUtils.getExtension(disposition.getFileName()));
        try {
            FileUtils.copyInputStreamToFile(fileInputStream, upload);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "success";
    }
    

    第一种方式我们采用的是直接使用InputStream来提供上传文件的输入流;

    @POST
    @Path("image2")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public String upload2(@FormDataParam("file") FormDataBodyPart bp, @Context ServletContext ctx) {
        File upload = new File(ctx.getRealPath("/upload"), UUID.randomUUID().toString() + "."
                + FilenameUtils.getExtension(bp.getFormDataContentDisposition().getFileName()));
        try {
            FileUtils.copyInputStreamToFile(bp.getValueAs(InputStream.class), upload);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "success";
    }
    

    第二种方式,我们使用较为底层的FormDataBodyPart来完成。综合两种方式,更建议使用第一种方式来完成文件上传。

    下载

    下载整体还是比较简单的操作,重点在于下载的相应(repsonse)的构造。所以在Jersey的下载演示中,我们重点在于介绍Jersey怎么使用Response对象来构造特殊的响应。

    我们先来看一段基本的Jersey中用于下载的代码:

    @GET
    @Path("/images/{name}")
    public void showImg(@PathParam("name") String imageName, @Context HttpServletResponse response,
            @Context ServletContext ctx) throws IOException {
        response.setHeader("Content-disposition", "attachment;filename="+imageName);
        response.setHeader("Cache-Control", "no-cache");
        File f = new File(ctx.getRealPath("/upload"), imageName);
        FileUtils.copyFile(f, response.getOutputStream());
    }
    

    可以看到,这段代码其实和以前使用Servlet来完成下载没有任何区别,原因就在于我们利用了Jersey的@Context标签可以注入HttpServletResponse对象的特性来完成的下载(注意,演示代码没有考虑到文件是否存在之类的情况);

    在JAX-RS中,为我们提供了一个更为灵活的Response对象,通过建造模式(build pattern),能够非常方便的构造出适合的响应。我们先来看第一种使用Response完成下载的代码:

    @GET
    @Path("/images2/{name}")
    public Response showImg(@PathParam("name") String imageName, @Context ServletContext ctx) throws IOException {
        File f = new File(ctx.getRealPath("/upload"), imageName);
        if (!f.exists()) {
            return Response.status(Status.NOT_FOUND).build();
        } else {
            return Response.ok(f).header("Content-disposition", "attachment;filename=" + imageName)
                    .header("Cache-Control", "no-cache").build();
        }
    }
    

    这段代码关注几个点:
    1,资源方法返回了一个javax.ws.rs.core.Response类,这个类可以直接被JAX-RS作为一个Response输出;
    2,在代码中,如果文件找不到,我们直接使用Response类提供的status方法,构造了一个NOT_FOUND(404)状态码,并通过build方法创建一个Response对象返回,相当于就是构建了一个404状态码的响应。
    3,如果文件存在,我们调用了Response的ok方法,传入了文件对象,那么JAX-RS会自动的设置本次响应状态码为200,并把文件写到响应输出流中,其次,我们采用链式调用的方式,设置了响应头,最后还是通过build方法,输出响应。总的来看,Response的这种操作方式,在开发上面还是非常方便和直观的。

    这段代码演示了怎么下载一个文件,对于某些没有源文件,直接输出流的内容,比如在内存中构造一个图片提供下载,或者构造一个Excel下载或者构造一个PDF文件提供下载等,就不能直接把一个原始File对象作为ok方法的参数,这个时候就需要使用到JAX-RS提供的StreamingOutput接口:

    @GET
    @Path("/images3/{name}")
    public Response showImg2(@PathParam("name") String imageName, @Context ServletContext ctx) throws IOException {
        final File f = new File(ctx.getRealPath("/upload"), imageName);
        if (!f.exists()) {
            return Response.status(Status.NOT_FOUND).build();
        } else {
            return Response.ok(new StreamingOutput() {
                @Override
                public void write(OutputStream output) throws IOException, WebApplicationException {
                    output.write(FileUtils.readFileToByteArray(f));
                }
            }).header("Content-disposition", "attachment;filename=" + imageName)
              .header("Cache-Control", "no-cache").build();
        }
    }
    

    在这端代码中,重点在于Response对象的ok方法,我们传入了一个匿名内部类,实现了StreamingOutput接口,在方法write中,直接把byte内容输出到了output参数中,这个output即为response的输出流,即可。

    关于Response对象的更多方法,可以参考其API文档,方法的名称大都是见名知意的,使用非常方便。

    小结

    本节简单的阐述了在Jersey中对文件的上传下载的基本操作,需要重点注意的是,要在Jersey中对上传进行操作,必须要引入MultipartFeature,才能针对multipart/form-data等特殊请求编码格式进行处理。

    WechatIMG7.jpeg

    相关文章

      网友评论

        本文标题:Jersey 开发RESTful(十二)Jersey上传下载

        本文链接:https://www.haomeiwen.com/subject/xkchzxtx.html