每次換手機,把舊手機的數據遷移到新手機就是個很麻煩的事情,幸好最近華為的“手機克隆”APP越來越強大,居然能夠把微信的聊天記錄包括圖片原封不動地遷移到新手機上,以前用微信自帶的聊天記錄轉移功能只能轉移文字信息,圖片視頻全部丟失,不知道現在的怎么樣。手機克隆還能把SD卡的內容也轉移過來,基本滿足了需要。
但是要把手機上的東西傳到電腦就沒那么簡單了,現在已經沒有了以前的大容量存儲模式,只能選擇MTP模式,這種模式其實不是一個完整的文件系統,有很多限制,所以一些傳統的軟件讀取不到,例如FastCopy是用不了的,用Windows自帶的文件管理器來復制,開始計算時間就要等很久,中間出了個錯就前功盡棄;還有通過手機上的APP訪問電腦共享的方式,在手機上復制也可以,但是同樣會莫名其妙卡死,FTP同理,折騰了好久,還是覺得自己動手比較好。
MTP協議在維基百科里解釋得比較清楚:
https://en.wikipedia.org/wiki/Media_Transfer_Protocol ,簡單點說就是:
1、不是以塊設備的形式訪問,跟U盤不同;
2、只能單線程訪問,不能同時進行多個操作,只能一個接一個;
3、控制權在設備上,對外展示的內容由設備決定;
4、默認不能直接對文件進行部分修改,只能復制過來修改完再復制回去,但Android對協議做了擴展,能夠修改部分文件內容;
5、在Linux上有些軟件能夠把它掛載為文件系統,這樣其他軟件就能像訪問普通文件系統一樣訪問了,但是Windows下似乎沒有。
不過有人開發了一個在Windows下通過JNI實現的Java庫jmtp,項目托管在Google Code,被墻了,但是GitHub有人fork了一個,可以下載下來,我下載的是
https://github.com/reindahl/jmtp
里面包含了C++的代碼和Java的代碼,以及兩個已經編譯好的dll文件,分別用于Win32和Win64,把其中一個dll文件放在工程目錄下,再把Java源代碼加入工程中即可使用,文檔比較簡陋,但是看test目錄下的MtpTest.java,基本可以摸到如何使用了,這個協議比較簡單,其實沒什么功能,我要的只是把文件復制到電腦上。
根據MtpTest.java,稍微修改一下,做個遞歸復制即可把手機上的所有文件復制到電腦上:
package test;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.rmi.server.SocketSecurityException;
import java.util.ArrayList;
import jmtp.PortableDevice;
import jmtp.PortableDeviceFolderObject;
import jmtp.PortableDeviceManager;
import jmtp.PortableDeviceObject;
import jmtp.PortableDeviceStorageObject;
public class TestApp {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<PortableDeviceStorageObject> devices = new ArrayList<>();
PortableDeviceManager manager = new PortableDeviceManager();
for (PortableDevice device : manager) {
System.out.println(device);
device.open();
// Iterate over deviceObjects
for (PortableDeviceObject object : device.getRootObjects()) {
String storageName=object.getName();
System.out.println(storageName);
// If the object is a storage object
if (object instanceof PortableDeviceStorageObject) {
PortableDeviceStorageObject storage = (PortableDeviceStorageObject) object;
System.out.println(storage.getChildObjects().length);
for (PortableDeviceObject child : storage.getChildObjects()) {
copyall(child,"E:\\手機備份\\"+object.getName());
}
}
}
device.close();
System.out.println(size);
}
}
public static void copyall(PortableDeviceObject obj,String path) {
if(obj instanceof PortableDeviceFolderObject) {
String objName=obj.getName();
if(objName.contains(":")) {
objName=objName.replace(':', ':');
}
String newPath = path+"\\"+objName;
System.out.println("創建文件夾:"+newPath);
File file = new File(newPath);
if(!file.exists()) {
file.mkdirs();
}
for(PortableDeviceObject subObj:((PortableDeviceFolderObject) obj).getChildObjects()) {
copyall(subObj,newPath);
}
}
else {
if(obj.getName().contains(":"))
return;
System.out.println("開始復制文件到:"+path+"\\"+obj.getName());
File file = new File(path);
obj.copy(file.toPath());
System.out.println("文件復制完成!");
}
}
}
其中發現有點問題:
1、Android設備文件名里是可以包含冒號(:)的,但Windows是不可以的,所以復制到這些文件的時候會有問題,于是遇到目錄名這樣就把它改為中文的冒號(:),但是遇到文件名這樣就不行了,因為這個庫的copy函數只需要指定目標目錄,不需要指定目標文件名,所以這些文件只能放棄;
2、Android手機的MTP協議是由“媒體存儲”這個系統APP控制的,有時候手機上可以看到的文件,通過MTP訪問卻怎么也看不到,重啟手機也不行,應該就是這個APP沒有更新數據,需要把它的系統數據清除掉,等它重建完重新訪問就可以看到了,不過這個重建時間非常長,可以查看它數據占用的空間,剛清除之后會發現它占用的空間會不斷增長,到了不增長的時候就是重建完了,就可以正常訪問了;
3、這個庫有時候還有點bug,有一次發現它讀取到的文件和文件夾都沒有了最后一個.后面的部分,所以總是卡住,重新插拔一下手機數據線又沒問題了;
4、為了避免復制了半天結果發現不完整,又要重來,最好在復制前先統計一下文件大小,看看跟手機上看到的占用存儲空間是不是一致,對于MTP設備上的文件,可以通過getSize函數得到大小,把上面復制操作改為大小累加即可,速度比復制快一些,不過由于小文件太多,也不會快很多。
把手機里的文件復制到電腦后,通過一些簡單的分析,發現有很多其實是垃圾來的,也可以為手機空間清理提供參考,因為在電腦上分析起來比在手機上方便一些。例如一些視頻APP的緩存,居然超過1G,占用了寶貴的內部存儲空間,之前一直都沒發現,通過電腦里的按文件大小搜索才發現。