最近在移動開發中遇到了一些文件下載的問題,實現后特地記錄一下,以備以后查閱。
最簡單的下載的實現方式是將文件的在網絡上的URL直接發送給手機,然后手機通過URL來請求這個文件,這么做有個缺點無法對請求的用戶進行准確的驗證。另一種方法是通過Action先對用戶的身份驗證通過后再發送文件給手持設備(請求端)。下面就來實現第二中方式。
- 服務器端非常簡單,就是寫個xml的配置文件,和實現一個簡單的action即可。
struts.xml的配置文件如下:
<result name="download" type="stream"> <param name="contentType">application/octet-stream</param> <param name="inputName">targetFile</param> <param name="contentDisposition">${suffix}</param> <param name="contentLength">${fileSize}</param> <param name="bufferSize">4096</param> </result>
說明:
contentType:指定文件的類型,application/octet-stream代表所有的文件類型,查看其他的文件類型描述;
inputName:文件的InputStream流,action中需要提供一個返回InputStream的getTargetFile()方法;
contentDisposition:下載文件的文件名,${suffix}指到action中獲取getSuffix()方法的返回值;也可以在這里放置filename=${fileName},這樣通過瀏覽器訪問的時候瀏覽器會自動查找到文件名,並顯示出來;
contentLength:下載的當前文件的大小,獲取方式同上,類型是long
bufferSize:緩存的大小
action對象實現如下:
/** * 文件的輸出流 * @return * @throws Exception */ public InputStream getTargetFile() throws Exception { java.io.File f = new java.io.File("D:\\test.avi"); if (f.exists()) { return new FileInputStream(f); } else { return null; } } /** * 設置返回的文件的名字 * @return */ public String getFileName() { return "test.avi"; } /** * 設置返回的文件的大小 * @return */ public long getFileSize() { java.io.File f = new java.io.File("D:\\test.avi"); return f.length(); } /** * 執行請求的邏輯處理,然后根據結果來判斷是否返回文件 * @return */ public String download() { //進行認證或其它的邏輯檢查 if(true){ return "download"; } return "error"; }
- 移動請求端實現如下:
iOS端
網絡訪問端代碼如下:
NSURL *downloadURL = [NSURL URLWithString:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:downloadURL]; NSHTTPURLResponse *response = nil; NSError *error = nil; NSData *resData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; if(error) { NSLog(@"請求下載文件失敗,錯誤信息:%@",error); return nil; } if(resData) { if(response && [response respondsToSelector:@selector(allHeaderFields)])
{ NSDictionary *httpResponseHeaderFields = [response allHeaderFields];//獲得相應的頭 long size = [[httpResponseHeaderFields objectForKey:@"Content-Length"] longLongValue];//獲得文件的大小,是由服務器在相應頭中傳遞過來的 NSString *fileSuffix = [httpResponseHeaderFields objectForKey:@"Content-Disposition"];//我在的服務器端設置的存放后綴名 VSFileUtil *fileUtil = [[VSFileUtil alloc]init];//自定義的一個文件工具類 NSString *filePath = [fileUtil writeToFileWithNSData:resData FileName:[fileId stringByAppendingFormat:@".%@",fileSuffix]];//創建文件 return filePath; } }
文件工具類的代碼如下:
NSArray *dir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);//獲得Library/Caches目錄,該目錄程序退出不會清空,iTunes也不備份此目錄 NSString *cDownloadBaseFolderPath = [[dir objectAtIndex:0] stringByAppendingPathComponent:@"FMADownload"];//創建Library/Caches/FMADownload目錄作為下載目錄 NSString *filePath = [cDownloadBaseFolderPath stringByAppendingPathComponent:file];//根據文件名和路徑構建出文件的絕對路徑 NSFileManager *fm = [NSFileManager defaultManager];//獲取文件管理器 [fm removeItemAtPath:filePath error:nil];//如果文件已經存在則移除該文件,文件不存在時也不會拋出異常,如果想捕獲提示信息,可以定義一個NSError傳遞給error參數即可 if([data writeToFile:filePath atomically:YES])//以原子處理的方式將內容寫進文件中 { return filePath; } else { return nil; }
Android端
這個由於是java開發的,這個過程將變得比iOS上簡單n倍,具體代碼如下:
public void download() throws Exception { InputStream is = null; BufferedInputStream bis = null; FileOutputStream fos = null; BufferedOutputStream bos = null; try { httpClient = new DefaultHttpClient(new BasicHttpParams()); HttpPost httpRequest = new HttpPost(validateURL);//validateURL是的請求地址 HttpResponse response = httpClient.execute(httpRequest); Header[] headers = response.getAllHeaders(); long size = 0;//文件大小 String suff = "";//文件后綴名 for(Header h : headers) { if("Content-Disposition".equals(h.getName())) { suff = h.getValue(); Log.i("janken", suff); } else if ("Content-Length".equals(h.getName())) { size = Long.valueOf(h.getValue()); Log.i("janken", size + ""); } } if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { throw new Exception("請求失敗"); } HttpEntity resEntity = response.getEntity(); is = resEntity.getContent();//獲得文件的輸入流 bis = new BufferedInputStream(is); File newFile = new File("/sdcard/test." + suff); fos = new FileOutputStream(newFile); bos = new BufferedOutputStream(fos); byte[] bytes = new byte[4096]; int len = 0;//最后一次的長度可能不足4096 while((len = bis.read(bytes)) > 0) { bos.write(bytes,0,len); } bos.flush(); } finally { if(bis != null)bis.close(); if(bos != null)bos.close(); if(fos != null)fos.close(); httpClient.getConnectionManager().shutdown(); } }