由于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;
}
文件转为string字符串参考:
用HttpUrlConnection模拟post表单进行文件上传平时很少使用,比较麻烦。
原理是: 分析文件上传的数据格式,然后根据格式构造相应的发送给服务器的字符串。
格式如下:这里的httppost123是我自己构造的字符串,可以是其他任何的字符串
----------httppost123 (\r\n)
Content-Disposition: form-data; name="img"; filename="t.txt" (\r\n)
Content-Type: application/octet-stream (\r\n)
(\r\n)
sdfsdfsdfsdfsdf (\r\n)
----------httppost123 (\r\n)
Content-Disposition: form-data; name="text" (\r\n)
(\r\n)
text tttt (\r\n)
----------httppost123-- (\r\n)
(\r\n)
上面的(\r\n)表示各个数据必须以(\r\n)结尾。
具体Java代码如下:
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
public
class HttpPostUtil{
URL url;
HttpURLConnection conn;
String boundary =
"--------httppost123";
Map textParams =
new HashMap();
Map fileparams =
new HashMap();
DataOutputStream ds;
public HttpPostUtil(String url) throws Exception{
this.url =
new URL(url);
}
//重新设置要请求的服务器地址,即上传文件的地址。
public void setUrl(String url) throws Exception{
this.url =
new URL(url);
}
//增加一个普通字符串数据到form表单数据中
public void addTextParameter(String name, String value){
textParams.put(name, value);
}
//增加一个文件到form表单数据中
public void addFileParameter(String name, File value){
fileparams.put(name, value);
}
// 清空所有已添加的form表单数据
public void clearAllParameters(){
textParams.clear();
fileparams.clear();
}
// 发送数据到服务器,返回一个字节包含服务器的返回结果的数组
public
byte[] send()
throws Exception {
initConnection();
try {
conn.connect();
}
catch (SocketTimeoutException e) {
// something
throw
new RuntimeException();
}
ds =
new DataOutputStream(conn.getOutputStream());
writeFileParams();
writeStringParams();
paramsEnd();
InputStream in = conn.getInputStream();
ByteArrayOutputStream out =
new ByteArrayOutputStream();
int b;
while ((b = in.read()) != -
1) {
out.write(b);
}
conn.disconnect();
return out.toByteArray();
}
//文件上传的connection的一些必须设置
private void initConnection() throws Exception{
conn = (HttpURLConnection)
this.url.openConnection();
conn.setDoOutput(
true);
conn.setUseCaches(
false);
conn.setConnectTimeout(
10000);
//连接超时为10秒
conn.setRequestMethod(
"POST");
conn.setRequestProperty(
"Content-Type",
"multipart/form-data; boundary=" + boundary);
}
//普通字符串数据
private void writeStringParams() throws Exception{
Set keySet = textParams.keySet();
for (Iterator it = keySet.iterator(); it.hasNext();) {
String name = it.next();
String value = textParams.get(name);
ds.writeBytes(
"--" + boundary +
"\r\n");
ds.writeBytes(
"Content-Disposition: form-data; name=\"" + name
+
"\"\r\n");
ds.writeBytes(
"\r\n");
ds.writeBytes(encode(value) +
"\r\n");
}
}
//文件数据
private void writeFileParams() throws Exception{
Set keySet = fileparams.keySet();
for (Iterator it = keySet.iterator(); it.hasNext();) {
String name = it.next();
File value = fileparams.get(name);
ds.writeBytes(
"--" + boundary +
"\r\n");
ds.writeBytes(
"Content-Disposition: form-data; name=\"" + name
+
"\"; filename=\"" + encode(value.getName()) +
"\"\r\n");
ds.writeBytes(
"Content-Type: " + getContentType(value) +
"\r\n");
ds.writeBytes(
"\r\n");
ds.write(getBytes(value));
ds.writeBytes(
"\r\n");
}
}
//获取文件的上传类型,图片格式为image/png,image/jpg等。非图片为application/octet-stream
private String getContentType(File f) throws Exception{
//return "application/octet-stream"; // 此行不再细分是否为图片,全部作为application/octet-stream 类型
ImageInputStream imagein = ImageIO.createImageInputStream(f);
if (imagein ==
null) {
return
"application/octet-stream";
}
Iterator it = ImageIO.getImageReaders(imagein);
if (!it.hasNext()) {
imagein.close();
return
"application/octet-stream";
}
imagein.close();
return
"image/" + it.next().getFormatName().toLowerCase();
//将FormatName返回的值转换成小写,默认为大写
}
//把文件转换成字节数组
private
byte[] getBytes(File f)
throws Exception {
FileInputStream in =
new FileInputStream(f);
ByteArrayOutputStream out =
new ByteArrayOutputStream();
byte[] b =
new
byte[
1024];
int n;
while ((n = in.read(b)) != -
1) {
out.write(b,
0, n);
}
in.close();
return out.toByteArray();
}
//添加结尾数据
private void paramsEnd() throws Exception{
ds.writeBytes(
"--" + boundary +
"--" +
"\r\n");
ds.writeBytes(
"\r\n");
}
// 对包含中文的字符串进行转码,此为UTF-8。服务器那边要进行一次解码
private String encode(String value) throws Exception{
return URLEncoder.encode(value,
"UTF-8");
}
public static void main(String[] args) throws Exception{
HttpPostUtil u =
new HttpPostUtil(
"http://localhost:3000/up_load");
u.addFileParameter(
"img",
new File(
"D:\\素材\\圆月.jpg"));
u.addTextParameter(
"text",
"中文");
byte[] b = u.send();
String result =
new String(b);
System.out.println(result);
}
}
如果不把中文转成UTF-8的格式进行传输,则后台显示中文乱码。
同样,如果其他参数包含中文,则也应当先转码。
当然,具体什么编码要和后台接收的编码一致。