上传大文件
大文件上传
在开发Android应用时,会经常上传图片到服务器,会经常出现上传图片失败和时间过长的问题
为了解决这个问题,决定把照片进行压缩后上保存后本地缓存文件夹后再上传
使用方法:
//压缩后得到新的图片路径
1. BitmapUtils.compressImageUpload
// 上传成功后删除缓存文件
BitmapUtils.deleteCacheFile()
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
/**
* Image compress factory class
*
* @author
*
*/
public class BitmapUtils {
/**
* 质量压缩方法
*
* @param image
* @return
*/
private static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
while (baos.toByteArray().length / 1024 > 100) {
baos.reset();
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
return bitmap;
}
/**
* 图片按比例大小压缩方法(根据路径获取图片并压缩)
*
* @param srcPath
* @return
*/
private static Bitmap getImage(String srcPath) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
// 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
float hh = 800f;// 这里设置高度为800f
float ww = 480f;// 这里设置宽度为480f
// 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;// be=1表示不缩放
if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;// 设置缩放比例
// 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
}
/**
* 将压缩的bitmap保存到SDCard卡临时文件夹,用于上传
*
* @param filename
* @param bit
* @return
*/
private static String saveMyBitmap(String filename, Bitmap bit) {
String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath()+"/laopai/";
String filePath = baseDir + filename;
File dir = new File(baseDir);
if (!dir.exists()) {
dir.mkdir();
}
File f = new File(filePath);
try {
f.createNewFile();
FileOutputStream fOut = null;
fOut = new FileOutputStream(f);
bit.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return filePath;
}
/**
* 压缩上传路径
* @param path
* @return
*/
public static String compressImageUpload(String path) {
String filename = path.substring(path.lastIndexOf("/") + 1);
Bitmap image = getImage(path);
return saveMyBitmap(filename, image);
}
/**
* 清除缓存文件
*/
public static void deleteCacheFile(){
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/laopai/");
RecursionDeleteFile(file);
}
/**
* 递归删除
*/
private static void RecursionDeleteFile(File file){
if(file.isFile()){
file.delete();
return;
}
if(file.isDirectory()){
File[] childFile = file.listFiles();
if(childFile == null || childFile.length == 0){
file.delete();
return;
}
for(File f : childFile){
RecursionDeleteFile(f);
}
file.delete();
}
}
}
上传过大文件100MB以上
- 采用随机读写进行文件分块上传
- 采用Socket进行上传大文件
分割上传
由于android自身的原因,对大文件(如影视频文件)的操作很容易造成OOM,即:Dalvik堆内存溢出,利用文件分割将大文件分割为小文件可以解决问题。
文件分割后分多次请求服务。
//文件分割上传
public void cutFileUpload(String fileType,String filePath)
{
try
{
FileAccessI fileAccessI = new FileAccessI(filePath, 0);
Long nStartPos = 0l;
Long length = fileAccessI.getFileLength();
int mBufferSize = 1024 * 100; //每次处理1024 * 100字节
byte[] buffer = new byte[mBufferSize];
FileAccessI.Detail detail;
long nRead = 0l;
String vedioFileName = Usual.f_getUUID(); //分配一个文件名
long nStart = nStartPos;
int i = 0;
while (nStart < length)
{
detail = fileAccessI.getContent(nStart);
nRead = detail.length;
buffer = detail.b;
JSONObject mInDataJson = new JSONObject();
mInDataJson.put("a", "282");
mInDataJson.put("FileName", vedioFileName);
mInDataJson.put("start", nStart); //服务端获取开始文章进行写文件
mInDataJson.put("filetype", fileType);
nStart += nRead;
nStartPos = nStart;
String url = UsualA.f_getXmlSOAUrl(UsualA.mServiceFastByteUrl, "n.uploadvedio", mInDataJson.toString(),
"282");
HttpFastUtil.f_httpPostByte(url, buffer, false);
}
}
catch (Exception e)
{
}
文件分割类
package ishitong.mppsp.android.util;
import java.io.*;
public class FileAccessI implements Serializable
{
RandomAccessFile oSavedFile;
long nPos;
public FileAccessI() throws IOException
{
this("", 0);
}
public FileAccessI(String sName, long nPos) throws IOException
{
oSavedFile = new RandomAccessFile(sName, "rw");//创建一个随机访问文件类,可读写模式
this.nPos = nPos;
oSavedFile.seek(nPos);
}
public synchronized int write(byte[] b, int nStart, int nLen)
{
int n = -1;
try
{
oSavedFile.write(b, nStart, nLen);
n = nLen;
}
catch (IOException e)
{
e.printStackTrace();
}
return n;
}
//每次读取102400字节
public synchronized Detail getContent(long nStart)
{
Detail detail = new Detail();
detail.b = new byte[102400];
try
{
oSavedFile.seek(nStart);
detail.length = oSavedFile.read(detail.b);
}
catch (IOException e)
{
e.printStackTrace();
}
return detail;
}
public class Detail
{
public byte[] b;
public int length;
}
//获取文件长度
public long getFileLength()
{
Long length = 0l;
try
{
length = oSavedFile.length();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return length;
}
}
服务端获得分割的文件,利用RandomAccessFile进行文件整理
/**
* 音视频图片处理
* @param mStr
* @return
* @throws Exception
*/
public static String f_uploadVedio(String mStr) throws Exception
{
String mResult = Usual.mEmpty;
String fileType = Usual.mEmpty;
int startPosL = 0;
RandomAccessFile oSavedFile = null;
JSONObject jsonObject = new JSONObject(mStr);
String vedioJsonStr = jsonObject.getString("VEDIO");
byte[] vedioBytes = Usual.f_fromBase64String(vedioJsonStr);
startPosL = (Integer) jsonObject.get("start"); //接收客户端的开始位置(文件读取到的字节大小)
fileType = (String)jsonObject.getString("filetype");
String fileName = (String)jsonObject.getString("FileName");
if(fileType.equals("picture"))
{
oSavedFile = new RandomAccessFile("E:\\"+fileName+".jpg","rw");
}
else if(fileType.equals("photo"))
{
oSavedFile = new RandomAccessFile("E:\\"+fileName+".jpg","rw");
}
else if(fileType.equals("voice"))
{
oSavedFile = new RandomAccessFile("E:\\"+fileName+".mp3","rw");
}
else if(fileType.equals("video"))
{
oSavedFile = new RandomAccessFile("E:\\"+fileName+".mp4", "rw");
}
//设置标志位,标志文件存储的位置
oSavedFile.seek(startPosL);
oSavedFile.write(vedioBytes);
oSavedFile.close();
mResult = "000";
return mResult;
}