通过Java WebService接口从服务端下载文件
一、 前言
本文讲述如何通过webservice接口,从服务端下载文件、报告到客户端。适用于跨系统间的文件交互,传输文件不大的情况(控制在几百M以内)。对于这种情况搭建一个FTP环境,增加了系统部署的复杂度和系统对外暴露的接口。通过在服务端读取文件,返回字节流到客户端的方式比较简单。
下面采用restful的接口形式,满足SOA架构接口要求。如下代码拷贝到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上运行,指定的下载文件路径和保存文件路径都是本机的文件路径。实际应用时,客户端可以指定一个服务端上的文件路径下载。
通过Java WebService接口从服务端下载文件的更多相关文章
- 【JMeter4.0学习(三)】之SoapUI创建WebService接口模拟服务端以及JMeter对SOAP协议性能测试脚本开发
目录: 创建WebService接口模拟服务端 下载SoapUI 新建MathUtil.wsdl文件 创建一个SOAP项目 接口模拟服务端配置以及启动 JMeter对SOAP协议性能测试脚本开发 [阐 ...
- 【转】SoapUI5.0创建WebService接口模拟服务端
原文:http://blog.csdn.net/a19881029/article/details/26348627 使用SoapUI创建WebService接口模拟服务端需要接口描述文件 MathU ...
- SoapUI5.0创建WebService接口模拟服务端(转)
转载自:https://blog.csdn.net/a19881029/article/details/26348627 使用SoapUI创建WebService接口模拟服务端需要接口描述文件 Mat ...
- java实现从服务端下载文件
这边用一个简单的servlet实现java从服务端下载文件的操作 写一个servlet: <servlet> <servlet-name>DownloadServlet< ...
- 从服务端下载文件到本地windows
之前常使用本地ubuntu和远程的centos服务器或者是本地mac和远程centos服务器通过命令scp或者nc来进行文件的传输. 现在用的是windows系统,欲将服务器的某文件load到本地. ...
- FTP 服务器在WIN10上的搭建及服务端下载文件实例
1.搭建 (1)控制面板--->程序----->将FTP服务器打勾 (2)输入iis,或者右键桌面-->管理-->服务和应用程序--->internet informat ...
- Java WebService 简单实例-服务端和客户端
转载自ITeye:https://www.iteye.com/topic/1135747/
- .net从服务端下载文件(可以断点续传)
public void DownFile(string guid) { var fileTransfer = new FileTransfer(); var directoryPath = Path. ...
- Java WebService接口生成和调用 图文详解>【转】【待调整】
webservice简介: Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的.专门的第三方软件或硬件, 就可相互交换数据或集成.依据Web Service规范实施的应用之间 ...
随机推荐
- Redis 持久化和配置文件
Reids 持久化 Redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File). RDB,简而言之,就是在不同的时间点,将redis ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(89)-EF执行SQL语句与存储过程
这一节,我们来看看EF如何执行SQL语句与读取存储过程的数据,可能有一部分人,还不知道EF如何执行存储过程与原生SQL语句! 我们什么时候要直接使用原生的SQL语句? 返回值过于复杂 过于复杂的联合查 ...
- Android App 压力测试方法(Monkey)
一.为什么要开展压力测试 a.提高产品的稳定性:b.提高产品的留存率 二.什么时候开展压力测试 a.首轮功能测试通过后:b.下班后的夜间进行 三.7个基础知识(理论部分) 3.1 手动测试场景与自动测 ...
- Linux 6.4 设置yum 为centOS源
一. 删除Redhat 自带的yum // root 用户执行 rpm -aq|grep yum|xargs rpm -e --nodeps 二 .下载CentOS 的 yum 安装文件 wget h ...
- Spring-Security+Freemarker 开启跨域请求伪造防护功能
CSRF简介--摘抄自<Spring实战(第4版)> 我们可以回忆一下,当一个POST请求提交到"/spittles"上时,SpittleController ...
- PHP操作Redis队列的方法
入队: <?php $redis = new Redis(); $redis->connect('127.0.0.1',6379); while(True){ try{ $value = ...
- 【Learning】 欧拉回路的求解
欧拉回路: 欧拉回路,俗称一笔画,比如一笔画五角星等. 这里给出非严谨的定义:欧拉回路即从一个点出发,不重复.不遗漏地经过所有的边与所有的点,并恰好回到出发点. 包含欧拉回路的图称为欧拉图. 欧拉回路 ...
- 遇到502错误,invalid request block size 解决方法
uWSGI是一个Web服务器,它实现了WSGI协议.uwsgi.http等协议.Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换. 因为业务需求,要最多输入350个汉字,在 ...
- 基于busybox的Linux小系统制作 (initrd)
我们有时候有需要在busybox基础上,制作linux,可是却不知道具体怎么做,这里将对基于busybox的linux小系统制作做出详细的步骤说明.准备环境:1.一个Redhat完整系统的虚拟机,本次 ...
- 使用ifstream和getline读取文件内容[c++]
转载:http://www.cnblogs.com/JCSU/articles/1190685.html 假设有一个叫 data.txt 的文件, 它包含以下内容: Fry: One Jillion ...