通过Java WebService接口从服务端下载文件「建议收藏」

通过Java WebService接口从服务端下载文件「建议收藏」一、前言本文讲述如何通过webservice接口,从服务端下载文件、报告到客户端。适用于跨系统间的文件交互,传输文件不大的情况(控制在几百M以内)。对于这种情况搭建一个FTP环境,增加了系统部署的复杂度和系统对外暴露的接口。通过在服务端读取文件,返回字节流到客户端的方式比较简单。下面采用restful的接口形式,满足SOA架构接口要求。如下代码拷贝到eclipse中即可运行,功能自测试

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

一、 前言

本文讲述如何通过webservice接口从服务器下载文件到客户端。适用于跨系统间的文件交互,传输文件不大的情况(控制在几百M以;);在这种情况下搭建一个FTP服务器增加了系统部署的复杂度和系统对外暴露的端口。采用在服务端读取文件,返回字节流到客户端再写入文件的方式比较简单。
下面的实现采用restful的接口方式,代码拷贝到eclipse中即可运行,功能自测试运行正常。测试样例代码的服务端和客户端在同一台PC上运行,放到不同PC上运行改一下发布服务和请求服务的IP地址即可。

二、 环境准备

2.1 CXF组件:用于发布WebService服务的开源组件,内部自带jetty Web容器。百度一下官网下载。
2.2 Eclipse:Java开发IDE。

三、 文件下载服务端开发

3.1 新建服务端Java项目,导入CXF lib目录下的Jar包。

3.2 定义restful的WebService接口,用于下载文件。

/**
 * 下载报告文件WebService接口, 对于大于20M的文件分多次传输。这里不对文件先读取缓存,再分批返回;
 * 而是每次重新读取文件,目的是为了让本服务无状态,能够通过ngnix反向代理多个实例,解决服务的可靠性
 * 和负载均衡问题。这样做有一个风险,在分批传送过程中,如果文件被修改或者删除了将导致文件读取失败。
 * 
 * @author Elon
 * @version 1.0 2015-06-30
 */
@Path("/DownloadFileWS")
public class DownloadFileWS 
{       
    // 单次传送最大字节数20M。
    private final static int maxsize_once;
    
    static
    {
        maxsize_once = 1024 * 1024 * 20;
    }
    
    /**
     * 下载文件。读取文件内容转换为字节流,大于10M分多次传输。
     * @param req 请求参数
     * @return 下载响应
     */
    @POST
    @Path("/downloadFile")
    public DownloadFileResponseVO downloadFile(DownloadFileRequestVO req){
        try {
            return readFileByte(req);
        } catch (IOException e) {
            e.printStackTrace();
            DownloadFileResponseVO vo = new DownloadFileResponseVO();
            vo.setErrCode(DownloadErrCodeEnum.READ_FILE_EXCEPTION);
            return vo;
        }
    }
    
    /**
     * 读取文件内容,构建文件字节流返回对象。
     * @param req 请求参数
     * @return 读取文件返回值。
     * @throws IOException IO异常
     */
    private DownloadFileResponseVO readFileByte(DownloadFileRequestVO req) throws IOException { 
        
        DownloadFileResponseVO vo = new DownloadFileResponseVO();
        
        // 获取判断文件最近修改时间
        File fileObject = new File(req.getFilePath());
        final long fileLastModifiedTime = fileObject.lastModified();
        
        // 判断分批传过程中文件是否修改
        if (fileLastModifiedTime != req.getFileLastModifiedTime()
                && req.getFileLastModifiedTime() != -1) 
        {   
            vo.setErrCode(DownloadErrCodeEnum.FILE_HAS_MODIFIED_WHILE_DOWNLOAD);
            return vo;
        }
        
        // 读取文件字节流。
        ByteArrayOutputStream fileStream = new ByteArrayOutputStream(1024);
        FileInputStream file = new FileInputStream(req.getFilePath());
        
        byte[] readbuff = new byte[1024];
        while(file.read(readbuff) != -1) {
            fileStream.write(readbuff);
        }
        file.close();
        
        // 构建返回文件字节信息。超过20M, 一次只返回20M。
        final byte[] fileBuff = fileStream.toByteArray();
        int end = 0;
        if (fileBuff.length - req.getStart() > maxsize_once) {
            end = req.getStart() + maxsize_once;
            vo.setEof(false);
        } else {    
            end = fileBuff.length;
            vo.setEof(true);
        }
        
        // 拷贝[start, end)范围内的字节到返回值中。
        vo.setFileByteBuff(Arrays.copyOfRange(fileBuff, req.getStart(), end));
        vo.setStart(end);
        vo.setErrCode(DownloadErrCodeEnum.DOWN_LOAD_SUCCESS);
        vo.setFileLastModifiedTime(fileLastModifiedTime);
        
        fileStream.close();
        
        return vo;
    }
}

3.3 接口中使用输入参数、返回值、错误码定义

3.3.1 输入参数类型定义

/**
 * 下载文件请求参数类型。
 * 
 * @author Elon
 * @version 1.0 2015-06-30
 */
@XmlRootElement(name = "DownloadFileRequest")
public class DownloadFileRequestVO implements Serializable
{
    /**
     * 序列化编码
     */
    private static final long serialVersionUID = 3142085277564296839L;
    
    // 文件路径
    private String filePath;
    
    // 读文件数据起始位置
    private int start;
    
    // 第一次读取文件时间(用于在分批传送文件过程中判断文件是否被修改了)
    private long fileLastModifiedTime;
    
    DownloadFileRequestVO() 
    {
        setFilePath("");
        setStart(0);
        setFileLastModifiedTime(-1);
    }

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }
    
    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }
    
    public long getFileLastModifiedTime() {
        return fileLastModifiedTime;
    }

    public void setFileLastModifiedTime(long fileLastModifiedTime) {
        this.fileLastModifiedTime = fileLastModifiedTime;
    }
    
    @Override
    public String toString() {
        return "filePath: " + filePath + "\n"
                + "start: " + String.valueOf(start);
    }
}

3.3.2 返回值类型定义

/**
 * 文件下载接口返回值类型
 * 
 * @author Elon
 * @version 1.0 2015-06-30
 */
@XmlRootElement(name = "FileVO")
public class DownloadFileResponseVO implements Serializable
{
    /**
     * 序列化编号
     */
    private static final long serialVersionUID = -2218217669316014388L;
    
    // 文件字节流缓存
    private byte[] fileByteBuff;
    
    // 标识文件传输是否结束
    private boolean eof;
    
    // 下一批读取文件数据起始位置
    private int start;
    
    // 第一次读取文件时间(用于在分批传送文件过程中判断文件是否被修改了)
    private long fileLastModifiedTime;
    
    // 错误码
    private DownloadErrCodeEnum errCode;
    
    public DownloadFileResponseVO() {
        setFileByteBuff(null);
        setEof(false);
        setStart(0);
        setErrCode(DownloadErrCodeEnum.ERR_CODE_NA);
        setFileLastModifiedTime(-1);
    }
    
    public byte[] getFileByteBuff() {
        return fileByteBuff;
    }

    public void setFileByteBuff(byte[] fileByteBuff) {
        this.fileByteBuff = fileByteBuff;
    }

    public boolean isEof() {
        return eof;
    }

    public void setEof(boolean eof) {
        this.eof = eof;
    }

    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }
    
    public DownloadErrCodeEnum getErrCode() {
        return errCode;
    }

    public void setErrCode(DownloadErrCodeEnum errCode) {
        this.errCode = errCode;
    }

    public long getFileLastModifiedTime() {
        return fileLastModifiedTime;
    }

    public void setFileLastModifiedTime(long fileLastModifiedTime) {
        this.fileLastModifiedTime = fileLastModifiedTime;
    }
    
    @Override
    public String toString() {
        return "fileByteBuff: " + fileByteBuff.toString() + "\n"
                + "eof: " + String.valueOf(eof) + "\n"
                + "start: " + String.valueOf(start);
    }
}

3.3.3 错误码枚举类型定义

/**
 * 下载文件错误码
 * @author Elon
 * @version 1.0 2015-06-30
 */
public enum DownloadErrCodeEnum 
{
    DOWN_LOAD_SUCCESS, // 下载成功
    READ_FILE_EXCEPTION, // 读取文件异常
    FILE_HAS_MODIFIED_WHILE_DOWNLOAD, // 在分批下载文件的过程中文件发生了修改
    ERR_CODE_NA, // 无效错误码
}

3.3.4 发布restful服务

public class StartServer 
{
    public static void main(String[] args) 
    {
        publishWS();
    }

    private static void publishWS() 
    {
        JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
        bean.setAddress("http://10.61.67.246:10011/download");
        bean.setServiceBean(new DownloadFileWS());
        bean.create();
        
        // 阻塞线程、等待外部消息请求。
        while(true)
        {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

四、 文件下载客户端开发

4.1 新建客户端Java项目,导入CXF lib目录下的Jar包。

4.2 调用接口下载文件,文件字节流写入目标文件保存。

public class StartClient 
{
    public static void main(String[] args) throws IOException 
    {
        downloadFileClient();
    }

    private static void downloadFileClient() throws IOException 
    {
        DownloadFileRequestVO req = new DownloadFileRequestVO();
        req.setFilePath("D:\\TEMP\\测试报告压缩包文件.zip");
        req.setStart(0);
        
        // 循环下载文件字节流,直到下载完文件所有内容。
        FileOutputStream out = new FileOutputStream("D://TEMP/123.zip");
        while (true)
        {   
            WebClient webClient = WebClient.create("http://10.61.67.246:10011");
            webClient.encoding("UTF-8");
            Response response = webClient.path("download/DownloadFileWS/downloadFile")
                    .post(req);
            
            GenericType<DownloadFileResponseVO> vo = new GenericType<DownloadFileResponseVO>(){};
            DownloadFileResponseVO rsp = response.readEntity(vo);
            webClient.close();
            
            if (rsp.getErrCode() != DownloadErrCodeEnum.DOWN_LOAD_SUCCESS)
            {
                System.err.println("Download file err: " + rsp.getErrCode());
                break;
            }
            
            // 将字节流写到文件
            out.write(rsp.getFileByteBuff());
            
            if (rsp.isEof())
            {
                break;
            }
            else
            {
                req.setStart(rsp.getStart());
                req.setFileLastModifiedTime(rsp.getFileLastModifiedTime());
            }
        }
        
        // 输出、关闭文件
        try 
        {
            out.flush();
            out.close();
            System.err.println("Download file success!");
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }
}

上述代码为研究测试用,服务端和客户端都在本地PC上运行,指定的下载文件路径和保存文件路径都是本机的文件路径。实际应用时,客户端可以指定一个服务端上的文件路径下载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/183668.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 【NOIP2012提高组】国王游戏[通俗易懂]

    【NOIP2012提高组】国王游戏[通俗易懂]题目描述恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这n位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。国王不希望某一个大臣获得特别多…

    2022年8月22日
    15
  • 数组截取splice_splice返回值

    数组截取splice_splice返回值Javascript数组的splice方法介绍splice()方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。语法格式array.splice(start[,deleteCount[,item1[,item2[,…]]]])参数start必选指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于

    2022年9月25日
    3
  • waf(web安全防火墙)主要功能点

    waf(web安全防火墙)主要功能点注入攻击SQL注入防护:阻止恶意SQL代码在网站服务器上执行。命令注入防护:阻止攻击者利用网站漏洞直接执行系统命令。XPATH注入防护:阻止攻击者构造恶意输入数据,形成XML文件实施注入。LDAP注入防护:阻止攻击者将网站输入的参数引入LDAP查询实施注入。SSI注入防护:阻止攻击者将SSI命令在服务端执行,主要发生在.shtml,.shtm,.stm文件。缓冲区溢出防护:阻止请求中填入超过缓冲区容量的数据,防止恶意代码被执行。HPP攻击防护:阻止攻击者利用HPP漏洞来发起注入…

    2022年5月5日
    153
  • httpClient写简单的get请求访问百度网址和Springmvc本地controller

    httpClient写简单的get请求访问百度网址和Springmvc本地controller

    2021年7月19日
    89
  • 圆通数据库泄露_数据库分析

    圆通数据库泄露_数据库分析今天的航空运单查询,其实质疑N快递单信息。版权声明:本文博主原创文章,博客,未经同意不得转载。转载于:https://www.cnblogs.com/mengfanrong/p/4808225.html…

    2022年9月19日
    2
  • 基于speech模块的久坐提醒小程序「建议收藏」

    基于speech模块的久坐提醒小程序「建议收藏」每天在电脑前坐很长的时间,因为有时候太过投入一下子就过去了若干个小时,容易猝死。于是心血来潮的想要写一个防久坐提醒小程序:第一种模式(最简单模式),若输入伏案工作时间数值不对则产生一个错误并退出。代码如下:importspeechimporttimeclassDebug:def__init__(self):self.start_time=time.time()self.minutes=int(input(“How

    2022年9月30日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号