(点击上方公众号,可快速关注)
来源:伯乐在线专栏作者 - Code4Android
链接:http://android.jobbole.com/85205/
点击 → 了解如何加入专栏作者
接上文
自定义FileBody
/**
* Created by xiehui on 2016/10/13.
*/
public class CustomFileBody extends AbstractContentBody {
private File file = null;
private int chunk = 0; //第几个分片
private int chunks = 1; //总分片数
private int chunkLength = 1024 * 1024 * 1; //分片大小1MB
public CustomFileBody(File file) {
this(file, "application/octet-stream");
}
public CustomFileBody(ChunkInfo chunkInfo) {
this(new File(chunkInfo.getFilePath()), "application/octet-stream");
this.chunk = chunkInfo.getChunk();
this.chunks = chunkInfo.getChunks();
this.file = new File(chunkInfo.getFilePath());
if (this.chunk == this.chunks) {
//先不判断,固定1M
//this.chunkLength=this.file.length()-(this)
}
}
public CustomFileBody(File file, String mimeType) {
super(mimeType);
if (file == null) {
throw new IllegalArgumentException("File may not be null");
} else {
this.file = file;
}
}
@Override
public String getFilename() {
return this.file.getName();
}
@Override
public String getCharset() {
return null;
}
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
@Override
public String getTransferEncoding() {
return "binary";
}
@Override
public long getContentLength() {
return chunkLength;
}
@Override
public void writeTo(OutputStream out) throws IOException {
if (out == null) {
throw new IllegalArgumentException("Output stream may not be null");
} else {
//不使用FileInputStream
RandomAccessFile randomAccessFile = new RandomAccessFile(this.file, "r");
try {
//int size = 1024 * 1;//1KB缓冲区读取数据
byte[] tmp = new byte[1024];
//randomAccessFile.seek(chunk * chunkLength);
if (chunk+1chunks){//中间分片
randomAccessFile.seek(chunk*chunkLength);
int n = 0;
long readLength = 0;//记录已读字节数
while (readLength chunkLength - 1024) {
n = randomAccessFile.read(tmp, 0, 1024);
readLength += 1024;
out.write(tmp, 0, n);
}
if (readLength chunkLength) {
n = randomAccessFile.read(tmp, 0, (int)(chunkLength - readLength));
out.write(tmp, 0, n);
}
}else{
randomAccessFile.seek(chunk*chunkLength);
int n = 0;
while ((n = randomAccessFile.read(tmp, 0, 1024)) != -1) {
out.write(tmp, 0, n);
}
}
out.flush();
} finally {
randomAccessFile.close();
}
}
}
public File getFile() {
return this.file;
}
}
文件分块上传模型类ChunkInfo
/* Created by xiehui on 2016/10/21.
*/
public class ChunkInfo extends FileInfo implements Serializable{
/**
* 文件的当前分片值
*/
private int chunk=1;
/**
* 文件总分片值
*/
private int chunks=1;
/**
* 下载进度值
*/
private int progress=1;
public int getChunks() {
return chunks;
}
public void setChunks(int chunks) {
this.chunks = chunks;
}
public int getChunk() {
return chunk;
}
public void setChunk(int chunk) {
this.chunk = chunk;
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
this.progress = progress;
}
@Override
public String toString() {
return "ChunkInfo{" +
"chunk=" + chunk +
", chunks=" + chunks +
", progress=" + progress +
'}';
}
}
具体上传实现
public String uploadFile() {
String retMsg = "1";
CustomMultipartEntity mpEntity = new CustomMultipartEntity(
new CustomMultipartEntity.ProgressListener() {
@Override
public void transferred(long num) {
Intent intent2 = new Intent();
ChunkInfo chunkIntent = new ChunkInfo();
chunkIntent.setChunks(chunkInfo.getChunks());
chunkIntent.setChunk(chunkInfo.getChunk());
chunkIntent.setProgress((int) num);
intent2.putExtra("chunkIntent", chunkIntent);
intent2.setAction("ACTION_UPDATE");
context.sendBroadcast(intent2);
}
});
try {
mpEntity.addPart("chunk", new StringBody(chunkInfo.getChunk() + ""));
mpEntity.addPart("chunks", new StringBody(chunkInfo.getChunks() + ""));
mpEntity.addPart("fileLength", new StringBody(chunkInfo.getFileLength()));
mpEntity.addPart("md5", new StringBody(chunkInfo.getMd5()));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
CustomFileBody customFileBody = new CustomFileBody(chunkInfo);
mpEntity.addPart("file", customFileBody);
HttpPost post = new HttpPost(actionUrl);
// 发送请求体
post.setEntity(mpEntity);
DefaultHttpClient dhc = new DefaultHttpClient();
try {
dhc.getParams().setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
HttpResponse response = dhc.execute(post);
int res = response.getStatusLine().getStatusCode();
switch (res) {
case 200:
//流形式获得
StringBuilder builder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
for (String s = bufferedReader.readLine(); s != null; s = bufferedReader.readLine()) {
builder.append(s);
}
retMsg = builder.toString();
break;
case 404:
retMsg = "-1";
break;
default:
retMsg = "500";
}
} catch (Exception e) {
e.printStackTrace();
}
return retMsg;
}
到此文件分块上传已基本完毕。那么此时你可能会问秒传的实现在哪了呢?别激动,在前面的分析中我们上传的参数有一个是md5,我们上传文件后将此值保存在数据库,以及图片的url链接,那么当我们上传文件之前先通过这个调用一个接口并上传参数md5,服务接口查询数据库是否有此md5的文件,如果有的话,直接将图片url返回即可,此时就提示用户文件上传成功,如果数据库没有此md5文件,则上传文件。
接口延伸
由于客户端上传的是文件块,当最后一块上传完成后,如果接口是每一分块保存了一个临时文件,则需要对分块的文件进行合并及删除。这个服务器FileChannel进行进行读写,当然也可以使用RandomAccessFile,因为我们上传了文件的总大小,则接口接收到分块文件时直接创建一个文件并调用randomAccessFile.setLength();方法设置长度,之后通过上传的seek方法在指定位置写入数据到文件即可。
到此,本篇文章真的结束了,若文章有不足或者错误的地方,欢迎指正,以防止给其他读者错误引导
专栏作者简介( 点击 → 加入专栏作者 )
Code4Android:简介还没来得及写 :)
打赏支持作者写出更多好文章,谢谢!
关注「安卓开发精选」
看更多精选文章
↓↓↓