在应用系统中集成文件上传下载的技术解决方案
来源:互联网 发布:立体画图软件 编辑:程序博客网 时间:2024/05/18 18:55
1 概述
在SAT5.0系统应用中,文件上传下载的情况越来越多,文件类型与大小也各不相同,客户端也有多种形式(主要是C#),这就需要提出一个SAT5.0的文件上传下载解决方案,在系统内部比较完善地解决这个问题。
根据实际使用经验,在系统应用初期,绝大部分是常见办公文档文件(4M以下),采用Web Service方式即可满足需要。其优势在于一方面能与本公司在SAT5.0中采用的技术框架(Java SSH+WebService/C#+WinForm)融合,形成一个整体,并可以扩展文件传输相关业务功能(文件传输时的前置与后置业务处理,如保存到数据库中、验证文件内容等等),从而在文件传输过程中将相关业务在一次调用中全部完成,避免将传输文件与对文件内容的业务操作划分成两个阶段,减少程序员开发与维护负担,并保持业务的完整、安全与高效;另一方面不用再另外部署配置FTP服务器或者其他组件,文件传输功能与SAT5.0系统统一安装配置即可,从而减少实施维护人员负担;最后,也能方便用户操作,给用户一个统一的文件传输及相关业务功能操作界面。
但是,随着用户不断提出新需求,不断深入开发系统应用功能,还可能需要上传下载大中型(4M至1G)、甚至巨型文件(1G以上),这对我们系统提出了更高的性能要求。
我们知道,通过Web Service方式有个不利的因素,它需要分配相同大小的内存来进行文件传输,对应用服务器的压力比较大,特别是多用户同时上传下载时,容易造成内存溢出,在性能明显上不能满足系统需要。因此,需要采用其他方式来解决以上问题。
针对以上大文件传输问题,经过研究分析,发现可以采用两种方式来解决,一是将FTP服务线程集成到SAT5.0系统中,开放服务器上的21端口,由客户端直接通过标准FTP协议来上传下载文件。二是另外设立专门的独立文件服务器(最好支持断点续传、多线程等高效文件传输方式)来解决这方面的问题。
另外特别要指出的是,随着与第三方应用系统集成任务的不断增多,例如银行系统、医院系统、学校系统等等,可能许多第三方应用由于受其开发商技术框架、开发水平、业务模式所限,只能采用FTP方式来上传下载文件,这就需要我们在系统中实现这一文件上传下载功能,最好同时支持FTP与Web Service两种方式。
2 Web Service方式
对于常见文档文件(4M以下)支持得很好,并能与SAT5.0架构紧密集成。
2.1 基本原理
通过Web Service的调用参数与返回值作为传递文件的方式。
上传文件时,将文件内容序列化为byte[](也可以进一步将内容Base64进行编码,防止保持原文件内容的byte[]在经过安全网关时未能通过病毒木马检测而被拦截或者被不正确地转码),作为调用参数传上去(在服务端进行解码);
下载文件时,将文件内容序列化为byte[](也可以进一步将内容Base64进行编码,理由同上),作为返回值保存为相应文件(如果在服务端Base64编码,客户端同样要进行Base64解码)。
如果只是在局域网内部使用,或者不需要经过严格的安全网关,可以在源代码文件中将文件内容编码与解码的有关语句去掉即可。
附录中给出了一个直接使用byte[]传输文件内容,没有加上Base64编码与解码的简单例子。
2.2 使用方式
通过xfire的Web Service方式进行发布。如果在文件上传下载操作中有大量前置(即文件传输前的准备业务)与后置(即文件传输成功完成后的扫尾业务),可以从以下基础源代码中派生其他类。
客户端用C#的Web Service的客户端直接访问即可。
要注意,在调用上传文件接口时,要将文件内容在C#客户端中使用Base64. encode()方法进行编码后上传,服务端要使用Base64.decode()解码得到真正文件内容。
在调用下载文件接口时,在服务端是已经将文件内容Base64.encode()编码的,要在C#客户端中使用Base64.decode()方法来得到真正文件内容。
如果在客户端没有办法实现C#的Base64. encode()/Base64.decode(),也可以直接使用不需要解码的版本(参见附录:BLOB方式实现文件上传下载)。
2.3 源码
2.3.1 服务端
2.3.1.1 IFileTransService接口类
public interface IFileTransService {
/**
* 文件上传
* @paramfileBuffer 必填,要上传的文件的字符串
* @paramstrRoot 选填,上传目录路径,可以是相对路径,在服务端可以根本不用管这个参数,直接根据服务端存储系统拼接成真实路径
* @paramfileName 必填,要上传的文件名
* @return上传成功则返回"ok",否则则返回"err:****"错误信息
*/
public String fileUpload(String fileBuffer,StringstrRoot,String fileName)throwsException;
/**
* 文件下载
* @paramstrRoot 选填,下载目录路径,可以是相对路径,在服务端可以根本不用管这个参数,直接根据服务端存储系统拼接成真实路径
* @paramfileName 必填,要下载的文件名
* @return成功则返回文件内容,否则则返回"err:****"错误信息
*/
public String downloadFile( String strRoot,StringfileName)throws Exception;
}
2.3.1.2 FileTransServiceImpl实现类
private static final int BUFFER_LENGTH = 1024 * 256;//缓冲区大小,256KB
/**
* 文件上传
* @paramfileByteBuf 必填,要上传的文件的字节流
* @paramstrRoot 选填,上传目录路径,可以是相对路径,在服务端可以根本不用管这个参数,直接根据服务端存储系统拼接成真实路径
* @paramfileName 必填,要上传的文件名
* @return上传成功则返回"ok",否则则返回"err:****"错误信息
*/
public String fileUpload(String fileBuffer, String strRoot, String fileName)throws Exception {
// 验证文件字符串是否为空
if (fileBuffer ==null || fileBuffer.length() == 0)
return"err:上传文件内容为空!";
// 验证上传目的地路径是否存在
if (strRoot ==null || strRoot == "")
return"err:上传目的地路径为空!";
// 验证指定的文件名是否为空
if (fileName ==null || fileName== "")
return"err:指定的文件名为空!";
if (fileName.lastIndexOf(".") == -1)
return"err:指定的文件名无后缀名!";
/**
* 声明四种I/O流
*/
ByteArrayInputStream fInStream = null;//文件输入流
BufferedInputStream bInStream = null;//输入缓冲流
OutputStream fOutStream = null;//输出流
BufferedOutputStream bOutStream = null;//输出缓冲流
try {
/**
* 赋值
*/
byte[] fileByteBuf = Base64.decode(fileBuffer);
// 得到字节数组输入流
fInStream = newByteArrayInputStream(fileByteBuf);
// 得到输入缓冲流
bInStream = newBufferedInputStream(fInStream);
// 判断上传的目的地路径是否是目录,不是则新建目录
File folderSave = new File(strRoot);
if (!folderSave.isDirectory())
folderSave.mkdirs();
// 验证目标文件
File fileSave = new File(strRoot +"/" + fileName);
if (fileSave.exists()){//当此抽象路径名表示的文件存在时
if (!fileSave.canWrite())
return"err:文件不可写入!";
}else if (!fileSave.createNewFile())
return"err:文件不能新建!";
// 得到输出流
fOutStream = newFileOutputStream(fileSave);
// 得到输出缓冲流
bOutStream = newBufferedOutputStream(fOutStream);
/**
* 循环
*/
byte[] fileData =new byte[BUFFER_LENGTH];//定义一个缓冲区
int intIndex = 0;
while ((intIndex = bInStream.read(fileData, 0,BUFFER_LENGTH)) != -1) {
bOutStream.write(fileData, 0, intIndex);
bOutStream.flush();
}
folderSave = null;
fileSave = null;
}catch (IOException e) {
return ("err:" + e.getMessage());
}finally{
/**
* 用完后,关闭I/O流
*/
try {
if (fInStream !=null) {
fInStream.close();// 关闭方法无效?!!!
fInStream = null;
}
if (bInStream !=null) {
bInStream.close();
bInStream = null;
}
if (fOutStream !=null) {
fOutStream.close();
fOutStream = null;
}
if (bOutStream !=null) {
bOutStream.close();
bOutStream = null;
}
} catch (IOException e) {
return ("err:" + e.getMessage());
}
}
return"ok";
}
/**
* 文件下载
* @paramstrRoot 选填,下载目录路径,可以是相对路径,在服务端可以根本不用管这个参数,直接根据服务端存储系统拼接成真实路径
* @paramfileName 必填,要下载的文件名
* @return成功则返回文件内容,否则则返回"err:****"错误信息
*/
public String downloadFile( String strRoot,String fileName)throws Exception {
String ret = null;
if (fileName !=null) {
File file = new File(strRoot, fileName);
if (file.exists()) {
FileInputStream fis = null;
StringBuffer sb = new StringBuffer();
try {
fis = new FileInputStream(file);
byte[] buffer =new byte[BUFFER_LENGTH]; //一次读入设置缓冲区大小的
int size=0;
while ((size=fis.read(buffer)) != -1) {
sb.append(Base64.encode(buffer,0,size));
}
ret = sb.toString();
System.out.println(ret);
} catch (FileNotFoundException e) {
e.printStackTrace();
ret="err:找不到要下载的文件!";
} catch (IOException e) {
e.printStackTrace();
ret="err:读取文件出错!";
} catch (Exception e) {
ret="err:"+ e.getMessage()+"!";
} finally {
fis.close();
}
}
}
return ret;
}
2.3.2 客户端测试
这里用了xfire的Web Service客户端建立及测试方法。如果采用其他方法,自行按照其步骤配置即可。
2.3.2.1 FileTransTest测试类
public class FileTransTest {
public static void main(String[] args) {
FileTransTest test = new FileTransTest();
test.upload();
test.download();
}
public void upload() {
try{
ApplicationContext ac = new ClassPathXmlApplicationContext("webservice-client.xml");
IFileTransService fileService =(IFileTransService)ac.getBean("FileTransService");
//文件
File file = new File("e://","我是原始的文件.pdf");
if (file.exists()) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
byte[] fileBytes=newbyte[(int)file.length()];
fis.read(fileBytes,0,(int)file.length());
fileService.fileUpload(Base64.encode(fileBytes),"E:\\","我是上传的文件.pdf");
} catch (FileNotFoundException e) {
e.printStackTrace();
thrownew Exception("出错啦!", e);
} catch (IOException e) {
e.printStackTrace();
thrownew Exception("出错啦!", e);
} catch (Exception e) {
thrownew Exception("出错啦!", e);
} finally {
fis.close();
}
}
}catch(Exception e){
e.printStackTrace();
}
}
public void download() {
try{
ApplicationContext ac = new ClassPathXmlApplicationContext("webservice-client.xml");
IFileTransService fileService =(IFileTransService)ac.getBean("FileTransService");
String data =fileService.downloadFile("e:\\","我是原始的文件.pdf");
//System.out.println(newString(Base64.decode(data)));
String targetFilePath = "e:\\我是下载的文件.pdf";
FileOutputStream data_fos = new FileOutputStream(targetFilePath);
data_fos.write(Base64.decode(data));
data_fos.flush();
data_fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
3 集成FTP服务线程方式
适合常见文档文件(4M以下)与大中型文件(4M到1G)。一般情况下也适合巨型文件(1G以上)。
但是,如果网络条件比较差,经常出现断线,则不能很好完成巨型文件的传输任务。
3.1 基本原理
这里提供一个虽然简单、但功能齐备的FTP服务器。采 Java语言编写,可以很简单地集成到SAT5.0应用服务器中。
可以根据SAT5.0的需求,在FtpConnection连接类中加入自己的业务方法,将文件上传下载及其相关前置与后置业务方法一气呵成。
3.2 使用方式
newFtpServer("C://","C://").start();//启动FTP Server线程
这里设置上传下载目录都为C盘根目录。
3.3 源码
3.3.1 服务端
3.3.1.1 FtpConnection连接类
importjava.net.*;
importjava.io.*;
importjava.util.*;
importjava.text.*;
public class FtpConnection extends Thread //继承线程
{
private String currentDir = "/"; // 当前目录
private Socketsocket; //客户套接字
private BufferedReaderreader = null; //读入的字符缓冲区
private BufferedWriterwriter = null; //写出的字符缓冲区
private String clientIP = null; //客户端IP地址
private SockettempSocket = null; //tempSocket用于传送文件
private ServerSocketpasvSocket = null; //用于被动模式
private String host = null;
private int port = (-1);
private String PassiveModeIp = ""; //被动模式IP
//需要设置的FTP服务器参数
static public String uploadRoot =null; /**上传主目录 */
static public String downloadRoot =null; /**下载主目录 */
static public String fileTimeOut ="60000"; //ftp超时设定,采用缺省值即可
static public String ftpPassiveModeIp="127.0.0.1";//被动模式IP地址,如果采用被动模式,则要设置此值
public FtpConnection(Socket socket)
{
this.socket = socket;
this.clientIP = socket.getInetAddress().getHostAddress();//获取客户端IP地址
}
public void run()
{
String command;
try
{
System.out.println(clientIP +" 已连接 ");
socket.setSoTimeout(Integer.parseInt(fileTimeOut)); //ftp超时设定
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//获取输入输出流
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
response("220-欢迎消息......"); //*****自定义
response("220-欢迎消息......"); //*****自定义
response("220 注意最后一行欢迎消息没有");
for(;;) //循环检测命令
{
command = reader.readLine(); //读入内容
if(command ==null)
break;
System.out.println("命令来自 " +clientIP + " 命令为: " + command);
parseCommand(command); //*****自定义
if(command.equals("QUIT")) //收到QUIT命令
break;
}
}
catch(Exception e) { e.printStackTrace(); }
finally
{ try
{
if(reader!=null)reader.close(); //关闭输入流
}
catch(Exception e) {}
try
{
if(writer!=null)writer.close(); //关闭输出流
}
catch(Exception e) {}
try
{
if(this.pasvSocket!=null)pasvSocket.close(); //关闭被动模式
}
catch(Exception e) {}
try
{
if(this.tempSocket!=null)tempSocket.close(); //关闭空套接字
}
catch(Exception e) {}
try
{
if(this.socket!=null)socket.close(); //关闭当前套接字
}
catch(Exception e) {}
}
System.out.println(clientIP +" 断开连接......");
}
private void response(String s) throws Exception //自定义:向客户发送消息
{
// System.out.println(" [RESPONSE] "+s);
writer.write(s);
writer.newLine();
writer.flush(); //注意要flush否则响应仍在缓冲区
System.out.println(s);
}
private static String pad(int length) //自定义:生成一个空字符串
{
StringBuffer buf = new StringBuffer();
for (int i = 0; i < length; i++)
buf.append((char)' ');
return buf.toString();//转换为String
}
private String getParam(String cmd, String start) //自定义:获取参数
{
String s = cmd.substring(start.length(), cmd.length());
return s.trim();
}
private StringtranslateUploadPath(Stringpath) //自定义:获取上传路径
{
if(path==null)return uploadRoot;
if(path.equals(""))return uploadRoot;//为空,返回主目录
path = path.replace('/', '\\');//替换
return uploadRoot + path;
}
private StringtranslateDownloadPath(Stringpath) //自定义:获取下载路径
{
if(path==null)return downloadRoot;
if(path.equals(""))return downloadRoot;//为空,返回主目录
path= path.replace('/', '\\');//替换
return downloadRoot + path;
}
//获取文件长度,注意是一个字符串,并定义为标准的12个字符
private String getFileLength(long length)
{
String s = Long.toString(length);
int spaces = 12 - s.length();
for(int i=0; i<spaces; i++)
s = " " + s;
return s;
}
//接下来便是处理用户命令,这个方法有点长,需要重构一下,我只是把LIST命令单独挪了出来:
private void parseCommand(String s)throws Exception
{
if(s==null || s.equals(""))
return;
if(s.startsWith("USER")) {
response("331 请输入密码");
}
else if(s.startsWith("PASS")) {
response("230 欢迎您的到来!");
}
else if(s.equals("QUIT")) {
response("221 欢迎再来!");
}
else if(s.equals("TYPEA")) {
response("200 TYPE set to A.");
}
else if(s.equals("TYPEI")) {
response("200 TYPE set to I.");
}
else if(s.equals("NOOP")) {
response("200 NOOP OK.");
}
else if(s.startsWith("CWD")) { // 设置当前目录,注意没有检查目录是否有效
this.currentDir = getParam(s,"CWD");//取CWD后的字符,即当前目录
response("250 CWD 命令执行成功.");
}
else if(s.startsWith("CDUP")) {
int n = this.currentDir.lastIndexOf("/");
this.currentDir =this.currentDir.substring(0,n);
response("250 CWD command succesful");
}
else if(s.equals("PWD")) { // 打印当前目录
response("257 \"" + this.currentDir +"\" 是当前目录.");
}
else if(s.startsWith("PORT")) {
// 记录端口
String[] params = getParam(s,"PORT").split(",");
if(params.length<=4 || params.length>=7)//限定端口号为4至7位
response("500 command param error.");
else {
this.host = params[0].trim() +"." +params[1].trim() + "." + params[2].trim() + "." + params[3].trim();//客户端IP
String port1 =null;
String port2 =null;
if(params.length == 6) {//???????
port1 = params[4].trim();
port2 = params[5].trim();
}
else {
port1 = "0";
port2 = params[4].trim();
}
this.port = Integer.parseInt(port1) * 256 + Integer.parseInt(port2);
response("200 command successful.");
}
}
else if(s.equals("PASV")) { // 进入被动模式
if(pasvSocket!=null)
pasvSocket.close();
try {
pasvSocket = new ServerSocket(0);
int pPort =pasvSocket.getLocalPort();
String s_port;
if(pPort<=255)
s_port = "255";
else {
int p1 = pPort / 256;
int p2 = pPort - p1*256;
s_port = p1 + "," + p2;
}
pasvSocket.setSoTimeout(60000);
if (!ftpPassiveModeIp.trim().equals("")){//为空
PassiveModeIp =ftpPassiveModeIp.trim(); //设置中的IP
}else{
PassiveModeIp =InetAddress.getLocalHost().getHostAddress();//实际主机IP
}
response("227 Entering Passive Mode ("+PassiveModeIp.replace('.',',')+ "," + s_port +")");
System.out.println("227 Entering Passive Mode ("+PassiveModeIp.replace('.',',')+ "," + s_port +")");
System.out.println("pasvSocket Created:" +pasvSocket.toString());
}
catch(Exception e) {
if(pasvSocket!=null) {
pasvSocket.close();
pasvSocket = null;
}
}
}
else if(s.startsWith("RETR")) { // 下载文件
String file = currentDir + (currentDir.endsWith("/") ?"" : "/") + getParam(s,"RETR");
System.out.println("download file: " + file);
Socket dataSocket;
// 根据上一次的PASV或PORT命令决定使用哪个socket
if(pasvSocket!=null)
dataSocket = pasvSocket.accept();
else
dataSocket = new Socket(this.host,this.port);
OutputStream dos = null;
InputStream fis = null;
response("150 Opening ASCII mode dataconnection.");
try {
fis = new BufferedInputStream(new FileInputStream(translateDownloadPath(file)));
dos = new DataOutputStream(newBufferedOutputStream(dataSocket.getOutputStream()));
// 开始正式发送数据:
byte[] buffer =new byte[20480];// 发送缓冲 20k
int num = 0;// 发送一次读取的字节数
do {
num = fis.read(buffer);
if(num!=(-1)) {
// 发送:
dos.write(buffer, 0, num);
dos.flush();
}
} while(num!=(-1));
fis.close();
fis = null;
dos.close();
dos = null;
dataSocket.close();
dataSocket = null;
response("226 transfer complete.");// 响应一个成功标志
}
catch(Exception e) {
response("550 ERROR: File not found or accessdenied.");
}
finally {
try {
if(fis!=null) fis.close();
if(dos!=null) dos.close();
if(dataSocket!=null) dataSocket.close();
}
catch(Exception e) {}
}
}
//
else if(s.startsWith("STOR")) { // 上传文件
String file = currentDir + (currentDir.endsWith("/") ?"" : "/") + getParam(s,"STOR");
System.out.println("download file: " + file);
Socket dataSocket;
// 根据上一次的PASV或PORT命令决定使用哪个socket
if(pasvSocket!=null){
dataSocket = pasvSocket.accept();
}else{
System.out.println("PASV模式的Socket为null");
dataSocket = new Socket(this.host,this.port);
}
OutputStream fis = null;
InputStream dos = null;
response("150 Opening ASCII mode dataconnection.");
try {
fis = new BufferedOutputStream(newFileOutputStream(translateUploadPath(file)));
dos = new DataInputStream(newBufferedInputStream(dataSocket.getInputStream()));
// 开始正式发送数据:
byte[] buffer =new byte[20480];// 发送缓冲 20k
int num = 0;// 发送一次读取的字节数
while((num = dos.read(buffer,0,1024))!=-1)
{
fis.write(buffer,0,num);
fis.flush();
}//上传到服务器
fis.close();
fis = null;
//
dos.close();
dos = null;
dataSocket.close();
dataSocket = null;
response("226 transfer complete.");// 响应一个成功标志
}
catch(Exception e) {
response("550 ERROR: File not found or accessdenied.");
}
finally {
try {
if(fis!=null) fis.close();
if(dos!=null) dos.close();
if(dataSocket!=null) dataSocket.close();
}
catch(Exception e) {}
}
}
else if(s.equals("LIST")) { // 列当前目录文件
Socket dataSocket;
// 根据上一次的PASV或PORT命令决定使用哪个socket
if(pasvSocket!=null){
System.out.println("pasvSocket has been Created,pasvSocket:" +pasvSocket.toString());
dataSocket = pasvSocket.accept();
} else{
System.out.println("pasvSocket not Created,startcreate:");
System.out.println("Create Socket,host=" +this.host +",port=" + this.port);
dataSocket = new Socket(this.host,this.port);
}
PrintWriter writer = new PrintWriter(newBufferedOutputStream(dataSocket.getOutputStream()));
response("150 Opening ASCII mode dataconnection.");
try {
responseList(writer, this.currentDir);
writer.close();
dataSocket.close();
response("226 transfer complete.");
}
catch(IOException e) {
writer.close();
dataSocket.close();
response(e.getMessage());
}
dataSocket = null;
}
else {
response("500 invalid command");// 没有匹配的命令,输出错误信息
}
}
//响应LIST命令
private void responseList(PrintWriter writer, String path) throws IOException {
File dir = new File(translateDownloadPath(path));
if(!dir.isDirectory())
throw new IOException("550No such file or directory");
File[] files = dir.listFiles();
String dateStr;
for(int i=0; i<files.length; i++) {
dateStr = new SimpleDateFormat("MMM dd hh:mm").format(new Date(files[i].lastModified()));
if(files[i].isDirectory()) {
writer.println("drwxrwxrwx 1 ftp System 0 "
+ dateStr + " " + files[i].getName());
}
else {
writer.println("-rwxrwxrwx 1 ftp System "
+ getFileLength(files[i].length())+ " " + files[i].getName());//dateStr + " " +
}
}
String file_header = "-rwxrwxrwx 1 ftp System 0 Aug 5 19:59 ";
String dir_header = "drwxrwxrwx 1 ftp System 0 Aug 15 19:59 ";
writer.println("total " + files.length);
writer.flush();
}
}
3.3.1.2 FtpServer服务类
importjava.net.*;
public class FtpServer extends Thread { //继承线程
public int FTP_PORT = 21; // 默认端口
ServerSocket ftpsocket = null;
public FtpServer(String uploadRoot,StringdownloadRoot){
FtpConnection.uploadRoot=uploadRoot;//共享上传目录
FtpConnection.downloadRoot=downloadRoot;//共享下载目录
}
public void run() {//重写RUN方法,创建多线程
Socket client = null;
try {
ftpsocket = new ServerSocket(FTP_PORT); //等待客户请求的服务器套接字
System.out.println("[info] listening port: " +FTP_PORT);
for(;;) { //无限循环,等客户端连接
client = ftpsocket.accept();
new FtpConnection(client).start();
}
}
catch(Exception e) {System.out.println("error");}//{ e.printStackTrace(); }
}
public static void main(String[] args) {
new FtpServer("C://","C://").start();//启动线程
}
}
3.3.2 客户端测试
支持常见FTP客户端。
4 独立FTP文件服务器方式
适用于巨型文件(1G以上)的上传下载。
缺点是要单独配置服务器。并且不能与SAT5.0系统紧密集成,需要在上传下载完文件后,再另行调用其他接口来完成业务操作。
在服务端要支持多线程下载、断点续传下载等功能。客户端要支持断点续传功能。
这种服务器产品化的解决方案比较多。传输协议一般有FTP与HTTP两种解决方案。还有采用TCP方式的,因为要穿防火墙的问题,不建议使用。
采用FTP协议方式的比较典型的是Serv-U FTP文件服务器,同时支持Windows系统与Unix、Linux系统。在C#客户端使用支持断点续传功能的FTP客户控件即可。
5 附录:BLOB方式实现文件上传下载
一个比较简单的方式,下载时返回byte[]对象,而不是Base64后的String对象。
5.1 IBlobFileTransService接口类
public interface IBlobFileTransService {
/*
* 文件上传服务
*/
public boolean uploadFile(String fileName,byte[] bytes) ;
/*
* 文件下载服务
*/
public byte[] downloadFile(String fileName) ;
}
5.2 BlobFileTransServiceImpl实现类
importjava.io.FileInputStream;
importjava.io.FileOutputStream;
importjava.io.IOException;
importcom.tbyf.filetrans.ws.IBlobFileTransService;
public class BlobFileTransServiceImpl implements IBlobFileTransService {
/*
* 文件上传服务
*/
public boolean uploadFile(String fileName,byte[] bytes)
{
FileOutputStream fos = null;
try{
fos = new FileOutputStream(fileName);
//将字节数组bytes中的数据,写入文件输出流fos中
fos.write(bytes);
fos.flush();
}catch (Exception e){
e.printStackTrace();
return false;
}finally{
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
/*
* 文件下载服务
*/
public byte[] downloadFile(String fileName)
{
String filepath = fileName;
FileInputStream in = null;
byte bytes[] =null;
try {
in = new FileInputStream(filepath);
bytes = newbyte[in.available()];
//从输入流in中,将 bytes.length 个字节的数据读入字节数组bytes中
in.read(bytes);
}catch (Exception e) {
e.printStackTrace();
}finally{
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
}
5.3 BlobFileTransTest测试类
importjava.io.FileInputStream;
importjava.io.FileOutputStream;
importjava.util.Date;
importjavax.xml.namespace.QName;
importorg.apache.axis2.addressing.EndpointReference;
importorg.apache.axis2.client.Options;
importorg.apache.axis2.rpc.client.RPCServiceClient;
public class BlobFileTransTest {
public static void main(String[] args)throws Exception
{
RPCServiceClient serviceClient = new RPCServiceClient();
Options options =serviceClient.getOptions();
EndpointReference targetEPR = new EndpointReference("http://127.0.0.1:8080/ChargeUnit3.2/services/BlobFileTransService");
options.setTo(targetEPR);
//=================测试文件上传==================================
String filePath = "E:\\我是原始的文件.pdf";
FileInputStream fis = newFileInputStream(filePath);
// 创建保存要上传的图像文件内容的字节数组
byte[] buffer =new byte[fis.available()];
//将输入流fis中的数据读入字节数组buffer中
fis.read(buffer);
//设置入参(1、文件名,2、文件字节流数组)
Object[] opAddEntryArgs = new Object[]{"E:\\我是上传的文件.pdf", buffer};
//返回值类型
Class<?>[] classes = new Class<?>[]{ Boolean.class };
//指定要调用的方法名及WSDL文件的命名空间
QName opAddEntry = new QName("http://ws.apache.org/axis2","uploadFile");
//关闭流
fis.close();
//执行文件上传
System.out.println(new Date()+"文件上传开始");
Object returnValue =serviceClient.invokeBlocking(opAddEntry,opAddEntryArgs, classes)[0];
System.out.println(new Date()+"文件上传结束,返回值="+returnValue);
//=================测试文件下载==================================
opAddEntry = new QName("http://ws.apache.org/axis2","downloadFile");
System.out.println(new Date()+"文件下载开始");
opAddEntryArgs = new Object[]{"E:\\我是上传的文件.pdf"};
byte bytes[] = (byte[]) serviceClient.invokeBlocking(opAddEntry,opAddEntryArgs,new Class[]{byte[].class})[0];
FileOutputStream fileOutPutStream = new FileOutputStream("E:\\我是下载的文件.pdf");
//将字节数组bytes中的数据,全部写入输出流fileOutPutStream中
fileOutPutStream.write(bytes);
fileOutPutStream.flush();
fileOutPutStream.close();
System.out.println(new Date()+"文件下载完成");
}
}
- 在应用系统中集成文件上传下载的技术解决方案
- 如何在Web页面中集成文件上传功能
- 如何在Web页面中集成文件上传功能
- Android中选取文件后在onActivityResult中将intent中的Uri转换成文件的路径
- 在RTF格式中提取图片并保存成文件
- JAVA用流在指定路径下生成文件
- ES6中集合的应用
- UML技术在基于Web的应用系统中的应用
- Flex中文件的上传下载
- Flex中文件的上传下载
- ssm 中文件的上传下载
- FLASH,FLV视频应用在JAVA系统下的解决方案
- u盘在linux系统下文件只读方式的解决方案
- u盘在linux系统下文件只读方式的解决方案
- u盘在linux系统下文件只读方式的解决方案
- u盘在linux系统下文件只读方式的解决方案
- matlab的数据如何保存成文件被VS调用
- gnuradio中把file_sink的二进制文件转换成文本文件
- netstat命令详解
- Jsp+Ajax生成页面验证码
- shutil模块
- 遍历文档节点
- 动态获得域名
- 在应用系统中集成文件上传下载的技术解决方案
- JAVA中extends 与 implements有啥区别?
- JDBC开发
- N97连接WIFI提示预置共享密钥无效
- Heap数组实现
- EXCEL2007中创建VBA代码
- 为什么证书和配置文件设置正确以后,xcode还是不能发现真机
- js定时循环实现秒表
- ios调用系统界面显示英文。。。