需求
把Android手機(jī)的mp3文件以TCP的傳輸方式傳遞到Win10的C#程序上,并且附帶歌名等信息。
PC端的要求是傳遞一個(gè)序列化后的文件過去,里面包含了文件本體和文件名等信息。
該序列化(C#這里的序列化好像和Java里面相差甚大啊!起初我是一頭霧水的)后的文件格式如下:
( ⊙o⊙ )? 什么鬼,沒辦法硬著頭皮試,起初根本不知道這是什么碼,因?yàn)樵贏ndroid里,傳遞文件最底層的就是字節(jié)流,那這個(gè)又是什么編碼呢。
嘗試
方式有很多種,比如把所有信息轉(zhuǎn)成字節(jié)傳遞過去,但是告知對(duì)方在哪里是什么,這種很麻煩;把文件轉(zhuǎn)成byte[]后放入一個(gè)類中,該類攜帶歌曲信息以byte[]發(fā)送過去。
嘗試了幾種方法:
1、 第一次(失敗)
ObjectOutputSteam object = new ObjectOutputStream(socket.getOutputStream());
通過對(duì)象流發(fā)送,發(fā)送過去后直接亂碼了,不用說,肯定是不能通過這樣的方式發(fā)送了,因?yàn)閷?duì)象流是JVM之間的一種傳遞方式,把對(duì)象以這種方式傳遞明顯行不通。
2、 第二次(失敗)
直接利用文件流把文件轉(zhuǎn)成byte[]然后傳遞過去,但是這種傳遞沒有辦法傳遞歌曲信息,嘗試用類來包裹
byte[] musicBytes = getBytes(filePath);//歌曲文件的字節(jié)數(shù)組
SimpleMusicEntity simpleMusicEntity = new SimpleMusicEntity();
simpleMusicEntity.setMusicBytes(musicBytes);
simpleMusicEntity.setTitle(title);
/**
* 獲得指定文件的byte數(shù)組
*/
public static byte[] getBytes(String filePath) {
byte[] buffer = null;
try {
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
byte[] b = new byte[1024];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
so,問題來了,我得到了一個(gè)類SimpleMusicEntity,里面富含了歌曲的所有信息(名稱、ext、文件字節(jié)),怎么傳呢。
用Gson轉(zhuǎn)對(duì)象為Json傳遞過去,成功了!就這么的,我測試了傳遞一曲“滴滴滴.mp3”成功了,對(duì)方從其中拿到byte[]數(shù)組轉(zhuǎn)文件也能播放成功。繼續(xù)測試,傳一首歌曲,直接OOM!錯(cuò)誤大概是消耗了幾十兆的空間導(dǎo)致App直接crash。
為什么呢,顯而易見,因?yàn)樽址L了!你把一個(gè)歌曲轉(zhuǎn)換成byte[]然后再轉(zhuǎn)換成String,你知道這個(gè)String.length()有多長么,不說了高興太早。
3、第三次
找了半天終于知道有這么一個(gè)東西“Base64”!
目前的做法是File轉(zhuǎn)byte[],byte[]通過轉(zhuǎn)為Base64的字符串,這樣得到的文件大小比原文件大1.2倍左右(拿首歌曲測試)
byte[] musicBytes = getBytes(filePath);//歌曲文件的字節(jié)數(shù)組
String str = new String(Base64.encode(musicBytes, Base64.NO_WRAP ));
SimpleMusicEntity2 simpleMusicEntity2 = new SimpleMusicEntity2();
simpleMusicEntity2.setMusicBytes(str);
simpleMusicEntity2.setTitle(title);
String string =simpleMusicEntity2.toString;
public class SimpleMusicEntity2{
private String title;
private String musicBytes;
//構(gòu)造一個(gè)json字符串
@Override
public String toString() {
return "{"
+ "\"title\":"
+ "\"" + title + '\"'
+ ","
+ "\"musicBytes\":"
+ "\"" + musicBytes + '\"' +
'}'+
"\r\n"
;
}
}
Base64簡單說一下
上面第二行“Base64.NO_WRAP”
為什么要用這個(gè),因?yàn)槿绻肂ase64.DEFAULT的話,我們的string里會(huì)自動(dòng)添加換行符“\n”
flag常量
Base64.CRLF 這個(gè)參數(shù)意思是Win風(fēng)格的換行符,意思就是使用CR LF這一對(duì)作為一行的結(jié)尾而不是Unix風(fēng)格的LF
Base64.DEFAULT 這個(gè)參數(shù)是默認(rèn),使用默認(rèn)的方法來加密
Base64.NO_PADDING 這個(gè)參數(shù)是略去加密字符串最后的”=”
Base64.NO_WRAP 這個(gè)參數(shù)意思是略去所有的換行符(設(shè)置后CRLF就沒用了)
Base64.URL_SAFE 這個(gè)參數(shù)意思是加密時(shí)不使用對(duì)URL和文件名有特殊意義的字符來作為加密字符,具體就是以-和_取代+和/
//習(xí)慣上使用Base64.NO_WRAP,使用什么方式編碼就需要使用什么方式解碼。
Base64Api
//將字節(jié)數(shù)組編碼,返回為String
Base64.encodeToString(byte[] bs,int flag);
//將字節(jié)數(shù)組編碼,返回字節(jié)數(shù)組
Base64.encode(byte[] bs,int flag);
//將字節(jié)數(shù)組按指定位置部分編碼,返回字符串
Base64.encodeToString(byte[] bs,int offset,int lenth);
//將字節(jié)數(shù)組按指定位置部分編碼,返回字節(jié)數(shù)組
Base64.encode(byte[] bs,int offset,int lenth);
//將編碼后的字符串解碼返回字節(jié)數(shù)組
Base64.decode(String str,int flag);
//將編碼后的字節(jié)數(shù)組解碼返回字節(jié)數(shù)組
Base64.decode(byte[],int flag);
//將編碼后的字節(jié)數(shù)組按指定位置部分解碼,返回字節(jié)數(shù)組
Base64.decode(byte[] bs,int offset,int len);
最后
發(fā)送方式,下面兩種方式都行:
byte[] bytes = string.getBytes();
//byte[] enter = "\r\n".getBytes();//這個(gè)換行符必須寫在字符串末端,不能單獨(dú)發(fā)送換行符
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
dos.write(bytes);
//dos.write(enter);
dos.flush();
dos.close();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write(string);
bufferedWriter.flush();
bufferedWriter.close();
終于PC端的C#程序能接收到文件信息,也能正確播放了,并且也有歌曲名字。
如果文件較大,可以先zip壓縮。如果還是較大,可以分段傳輸。
缺點(diǎn)
這種傳遞文件的做法不推薦,因?yàn)閭鬟f10M以上的文件極有可能OOM,因?yàn)镴ava虛擬機(jī)頂不住。
改進(jìn)
請(qǐng)看第二篇文章Java傳遞音頻給PC端C#程序(2)
這只是功能demo而已,其實(shí)Android還要考慮很多,比如ip是變化的,我怎么獲取對(duì)應(yīng)設(shè)備的ip,然后建立TCP連接等等。
喝水不忘挖井人
參考文獻(xiàn):
Android編碼解碼及其原理
文件與base64二進(jìn)制轉(zhuǎn)換
圖片與Base64相互轉(zhuǎn)換,c#與java通用
Byte[]和BASE64之間的轉(zhuǎn)換
對(duì)文件進(jìn)行base64編碼成字符串進(jìn)行保存或傳輸