需求
把Android手機(jī)的mp3文件以TCP的傳輸方式傳遞到Win10的C#程序上,并且附帶歌名等信息赌厅。
PC端的要求是傳遞一個(gè)序列化后的文件過(guò)去,里面包含了文件本體和文件名等信息轿塔。
該序列化(C#這里的序列化好像和Java里面相差甚大疤卦浮!起初我是一頭霧水的)后的文件格式如下:
( ⊙o⊙ )? 什么鬼勾缭,沒(méi)辦法硬著頭皮試揍障,起初根本不知道這是什么碼,因?yàn)樵贏ndroid里俩由,傳遞文件最底層的就是字節(jié)流亚兄,那這個(gè)又是什么編碼呢。
嘗試
方式有很多種采驻,比如把所有信息轉(zhuǎn)成字節(jié)傳遞過(guò)去审胚,但是告知對(duì)方在哪里是什么,這種很麻煩礼旅;把文件轉(zhuǎn)成byte[]后放入一個(gè)類(lèi)中膳叨,該類(lèi)攜帶歌曲信息以byte[]發(fā)送過(guò)去。
嘗試了幾種方法:
1痘系、 第一次(失敺谱臁)
ObjectOutputSteam object = new ObjectOutputStream(socket.getOutputStream());
通過(guò)對(duì)象流發(fā)送,發(fā)送過(guò)去后直接亂碼了,不用說(shuō)龄坪,肯定是不能通過(guò)這樣的方式發(fā)送了昭雌,因?yàn)閷?duì)象流是JVM之間的一種傳遞方式,把對(duì)象以這種方式傳遞明顯行不通健田。
2烛卧、 第二次(失敗)
直接利用文件流把文件轉(zhuǎn)成byte[]然后傳遞過(guò)去妓局,但是這種傳遞沒(méi)有辦法傳遞歌曲信息总放,嘗試用類(lèi)來(lái)包裹
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,問(wèn)題來(lái)了好爬,我得到了一個(gè)類(lèi)SimpleMusicEntity局雄,里面富含了歌曲的所有信息(名稱(chēng)、ext存炮、文件字節(jié))炬搭,怎么傳呢。
用Gson轉(zhuǎn)對(duì)象為Json傳遞過(guò)去穆桂,成功了宫盔!就這么的,我測(cè)試了傳遞一曲“滴滴滴.mp3”成功了充尉,對(duì)方從其中拿到byte[]數(shù)組轉(zhuǎn)文件也能播放成功。繼續(xù)測(cè)試衣形,傳一首歌曲驼侠,直接OOM!錯(cuò)誤大概是消耗了幾十兆的空間導(dǎo)致App直接crash谆吴。
為什么呢倒源,顯而易見(jiàn),因?yàn)樽址L(zhǎng)了句狼!你把一個(gè)歌曲轉(zhuǎn)換成byte[]然后再轉(zhuǎn)換成String笋熬,你知道這個(gè)String.length()有多長(zhǎng)么,不說(shuō)了高興太早腻菇。
3胳螟、第三次
找了半天終于知道有這么一個(gè)東西“Base64”!
目前的做法是File轉(zhuǎn)byte[]筹吐,byte[]通過(guò)轉(zhuǎn)為Base64的字符串糖耸,這樣得到的文件大小比原文件大1.2倍左右(拿首歌曲測(cè)試)
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簡(jiǎn)單說(shuō)一下
上面第二行“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)的方法來(lái)加密
Base64.NO_PADDING 這個(gè)參數(shù)是略去加密字符串最后的”=”
Base64.NO_WRAP 這個(gè)參數(shù)意思是略去所有的換行符(設(shè)置后CRLF就沒(méi)用了)
Base64.URL_SAFE 這個(gè)參數(shù)意思是加密時(shí)不使用對(duì)URL和文件名有特殊意義的字符來(lái)作為加密字符,具體就是以-和_取代+和/
//習(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è)換行符必須寫(xiě)在字符串末端,不能單獨(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)行保存或傳輸