在上一篇文章Thrift在Android上的客户端实现中,我介绍了应该如何通过Thrift实现Android平台对服务器代码的远程调用。本篇内容继续讲解如何通过Thrift实现Android平台向服务器传输文件。
一、定义接口
1.桌面上新建thrift接口文件FileIface.thrift
/**
* 文件名为FileIface.thrift
* 基于:thrift-0.10.0
**/
namespace java com.younghong.thriftTest //定义命名空间,把该类所处的包名写在这里
struct FileChunk{
1:string fileName
2:i64 offset
3:i64 size
4:binary data
}
service FileIface{
i64 uploadFile(1:FileChunk fileChunk)
}
2.把上面的thrift接口编译成java接口:运行cmd,输入“cd desktop”后回车,切换到FileIface.thrift所在的桌面路径。然后接着输入“thrift -gen Java FileIface.thrift”后回车,会在桌面生成gen-java文件夹,该文件夹中的FileIface.java文件就是我们需要的java接口,FileChunk.java是生成的Bean对象。
二、服务端实现
1.把刚才的java接口文件FileIface.java和FileChunk.java拷贝到项目中,然后新建类FileImp,实现FileIface.Iface 接口。
package com.younghong.thriftTest;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import org.apache.thrift.TException;
public class FileImp implements FileIface.Iface {
public long uploadFile(FileChunk fileChunk) throws TException {
// new出本地File
File file = new File("E:\\img.jpg");
try {
file.createNewFile();
RandomAccessFile stream = new RandomAccessFile(file, "rw");
stream.seek(fileChunk.offset);
System.out.println(fileChunk.offset+":"+fileChunk.size+":"+fileChunk.data.array().length);
FileChannel theFileChannel = stream.getChannel();
theFileChannel.write(fileChunk.data);
theFileChannel.force(true);
theFileChannel.close();
stream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}
}
2.在入口函数中开启服务。因为要同时开启两个服务,所以代码要进行部分改动,如下所示:
package com.younghong.thriftTest;
import org.apache.thrift.TMultiplexedProcessor;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.apache.thrift.transport.TTransportException;
/**
* Hello world!
*
*/
public class App {
public static void main(String[] args) {
try {
// 实现反转字符串的Processor,传入实现类ReverseImp
ReverseIface.Processor reverseProcessor = new ReverseIface.Processor(new ReverseImp());
FileIface.Processor fileProcessor = new FileIface.Processor(new FileImp());
// 构建Thrift通信协议,设置其中的参数
TServerTransport serverTransport = new TServerSocket(8080);
TMultiplexedProcessor processor = new TMultiplexedProcessor();
processor.registerProcessor("reverse", reverseProcessor);
processor.registerProcessor("uploadFile", fileProcessor);
// 根据设定好的参数,构建server,并启动server
TThreadPoolServer.Args arg = new TThreadPoolServer.Args(serverTransport);
arg.protocolFactory(new TBinaryProtocol.Factory());
arg.transportFactory(new TFramedTransport.Factory());
arg.processorFactory(new TProcessorFactory(processor));
TServer server = new TThreadPoolServer(arg);
server.serve();// 启动服务
} catch (TTransportException e) {
e.printStackTrace();
}
}
}
三、Android平台代码
1.添加读写SD卡权限,把刚才的java接口文件FileIface.java和FileChunk.java拷贝到项目中,修改MainActivity的onClick方法。(此处把button的功能直接修改成成了上传文件)
package com.younghong.thriftdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TMultiplexedProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClick(View view) {
//获取输入框的信息
EditText editText = (EditText) findViewById(R.id.editText);
final String strParam = editText.getText().toString();
//开启线程,执行网络访问操作
new Thread(new Runnable() {
@Override
public void run() {
try {
//构建Thrift传输和协议
TTransport tTransport = getTTransport();
TBinaryProtocol protocol = new TBinaryProtocol(tTransport);
TMultiplexedProtocol mp1 = new TMultiplexedProtocol(protocol, "uploadFile");
//构建客户端,传入字符串进行反转操作
FileIface.Client client = new FileIface.Client(mp1);
File file = new File(strParam);
FileChunk fileChunk = new FileChunk();
FileInputStream fis = new FileInputStream(file);
// 1.从FileInputStream对象获取文件通道FileChannel
FileChannel channel = fis.getChannel();
long size = channel.size();
// 2.从通道读取文件内容
ByteBuffer byteBuffer = ByteBuffer.allocate(8192);
// channel.read(ByteBuffer) 方法就类似于 inputstream.read(byte)
// 每次read都将读取 allocate 个字节到ByteBuffer
long offset = 0;
while (offset < file.length()) {
// channel.position(offset);
channel.read(byteBuffer);
byteBuffer.flip();
fileChunk.offset = offset;
fileChunk.data = byteBuffer;
fileChunk.size = file.length();
fileChunk.fileName = "";
System.out.println(fileChunk.offset + ":" + fileChunk.size);
long returnMsg = client.uploadFile(fileChunk);
byteBuffer.clear();
offset += 8192;
}
tTransport.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
//构建Thrift传输协议
private TTransport getTTransport() throws Exception {
try {
//主机、端口、超时
TSocket tSocket = new TSocket("172.16.36.146", 8080, 5000);
//根据socket构建thrift
TTransport tTransport = new TFramedTransport(tSocket);
if (!tTransport.isOpen()) {
tTransport.open();
}
return tTransport;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2.安装apk,在EditText中输入手机一张jpg图片的路径,点击Button后,如果能在电脑的E盘根目录打开img.jpg,则证明上传成功。
四、总结
thrift传输文件比较麻烦,并且不好控制。本文也只是实现了最简单的上传功能,性能、安全、异常捕获等内容未做详细判断。