『壹』 C#讀寫大文件,如何提速
不妨試試下面這個鏈接的方法
轉自http://www.cnblogs.com/criedshy/archive/2010/06/13/1757826.html
C#內存映射文件代碼
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Runtime.InteropServices;
usingSystem.IO;
namespaceTestOpenFileMap
{
publicclassFileMap
{
[StructLayout(LayoutKind.Sequential)]
internalstructSYSTEM_INFO
{
publicuintdwOemId;
publicuintdwPageSize;
;
;
;
;
publicuintdwProcessorType;
;
publicuintdwProcessorLevel;
publicuintdwProcessorRevision;
}
privateconstuintGENERIC_READ=0x80000000;
privateconstuintGENERIC_WRITE=0x40000000;
privateconstintOPEN_EXISTING=3;
privateconstintINVALID_HANDLE_VALUE=-1;
privateconstintFILE_ATTRIBUTE_NORMAL=0x80;
privateconstuintFILE_FLAG_SEQUENTIAL_SCAN=0x08000000;
privateconstuintPAGE_READWRITE=0x04;
privateconstintFILE_MAP_COPY=1;
privateconstintFILE_MAP_WRITE=2;
privateconstintFILE_MAP_READ=4;
///<summary>
///內存映射文件句柄
///</summary>
///<paramname="hFile"></param>
///<paramname="lpFileMappingAttributes"></param>
///<paramname="flProtect"></param>
///<paramname="dwMaximumSizeHigh"></param>
///<paramname="dwMaximumSizeLow"></param>
///<paramname="lpName"></param>
///<returns></returns>
[DllImport("kernel32.dll")]
(IntPtrhFile,
IntPtrlpFileMappingAttributes,uintflProtect,
uintdwMaximumSizeHigh,
uintdwMaximumSizeLow,stringlpName);
///<summary>
///內存映射文件
///</summary>
///<paramname="hFileMappingObject"></param>
///<paramname="dwDesiredAccess"></param>
///<paramname="dwFileOffsetHigh"></param>
///<paramname="dwFileOffsetLow"></param>
///<paramname="dwNumberOfBytesToMap"></param>
///<returns></returns>
[DllImport("kernel32.dll")]
(IntPtrhFileMappingObject,uint
dwDesiredAccess,uintdwFileOffsetHigh,uintdwFileOffsetLow,
uintdwNumberOfBytesToMap);
///<summary>
///撤消文件映像
///</summary>
///<paramname="lpBaseAddress"></param>
///<returns></returns>
[DllImport("kernel32.dll")]
(IntPtrlpBaseAddress);
///<summary>
///關閉內核對象句柄
///</summary>
///<paramname="hObject"></param>
///<returns></returns>
[DllImport("kernel32.dll")]
(IntPtrhObject);
///<summary>
///打開要映射的文件
///</summary>
///<paramname="lpFileName"></param>
///<paramname="dwDesiredAccess"></param>
///<paramname="dwShareMode"></param>
///<paramname="securityAttrs"></param>
///<paramname="dwCreationDisposition"></param>
///<paramname="dwFlagsAndAttributes"></param>
///<paramname="hTemplateFile"></param>
///<returns></returns>
[DllImport("kernel32.dll")]
(stringlpFileName,
uintdwDesiredAccess,FileSharedwShareMode,IntPtrsecurityAttrs,
FileModedwCreationDisposition,uintdwFlagsAndAttributes,IntPtrhTemplateFile);
///<summary>
///得到文件大小
///</summary>
///<paramname="hFile"></param>
///<paramname="highSize"></param>
///<returns></returns>
[DllImport("kernel32.dll",SetLastError=true)]
(IntPtrhFile,outuinthighSize);
///<summary>
///得到系統信息
///</summary>
///<paramname="lpSystemInfo"></param>
[DllImport("kernel32.dll",SetLastError=true)]
(refSYSTEM_INFOlpSystemInfo);
///<summary>
///使用內存文件映射得到文件內容
///</summary>
///<paramname="path">文件路徑</param>
///<returns></returns>
(stringpath)
{
StringBuildersb=newStringBuilder();
IntPtrfileHandle=CreateFile(path,
GENERIC_READ|GENERIC_WRITE,FileShare.Read|FileShare.Write,
IntPtr.Zero,FileMode.Open,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,IntPtr.Zero);
if(INVALID_HANDLE_VALUE!=(int)fileHandle)
{
IntPtrmappingFileHandle=CreateFileMapping(
fileHandle,IntPtr.Zero,PAGE_READWRITE,0,0,"~MappingTemp");
if(mappingFileHandle!=IntPtr.Zero)
{
SYSTEM_INFOsystemInfo=newSYSTEM_INFO();;
GetSystemInfo(refsystemInfo);
//得到系統頁分配粒度
uintallocationGranularity=systemInfo.dwAllocationGranularity;
uintfileSizeHigh=0;
//getfilesize
uintfileSize=GetFileSize(fileHandle,outfileSizeHigh);
fileSize|=(((uint)fileSizeHigh)<<32);
//關閉文件句柄
CloseHandle(fileHandle);
uintfileOffset=0;
uintblockBytes=1000*allocationGranularity;
if(fileSize<1000*allocationGranularity)
blockBytes=fileSize;
//分塊讀取內存,適用於幾G的文件
while(fileSize>0)
{
//映射視圖,得到地址
IntPtrlpbMapAddress=MapViewOfFile(mappingFileHandle,FILE_MAP_COPY|FILE_MAP_READ|FILE_MAP_WRITE,
(uint)(fileOffset>>32),(uint)(fileOffset&0xFFFFFFFF),
blockBytes);
if(lpbMapAddress==IntPtr.Zero)
{
returnsb;
}
//對映射的視圖進行訪問
byte[]temp=newbyte[blockBytes];
//從非託管的內存中復制內容到託管的內存中
Marshal.Copy(lpbMapAddress,temp,0,(int)blockBytes);
//用循環太慢了,文件有幾M的時候就慢的要死,還是用上面的方法直接
//for(uinti=0;i<dwBlockBytes;i++)
//{
//bytevTemp=Marshal.ReadByte((IntPtr)((int)lpbMapAddress+i));
//temp[i]=vTemp;
//}
//此時用ASCII解碼比較快,但有中文會有亂碼,用gb2312即ANSI編碼也比較快,16M的文件大概4秒就讀出來了
//但用unicode解碼,文件大的時候會非常慢,會現卡死的狀態,不知道為什麼?
//ASCIIEncodingencoding=newASCIIEncoding();
//System.Text.UnicodeEncodingencoding=newUnicodeEncoding();
//sb.Append(encoding.GetString(temp));
sb.Append(System.Text.Encoding.GetEncoding("gb2312").GetString(temp));
//撤消文件映像
UnmapViewOfFile(lpbMapAddress);
//修正參數
fileOffset+=blockBytes;
fileSize-=blockBytes;
}
//closefilemappinghandle
CloseHandle(mappingFileHandle);
}
}
returnsb;
}
}
}
『貳』 iOS 江湖救急!!!怎樣將音頻文件上傳到伺服器
http上傳 把文件以body形式上傳到伺服器 NSFileHandle* handler = [NSFileHandle fileHandleForReadingAtPath:_fileURL];
[handler seekToFileOffset:(unsigned long long)_range.location];
NSData* body = [handler readDataOfLength:_range.length];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:@"POST"];
[request setValue:@"video/mp4" forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"%d",body.length] forHTTPHeaderField:@"Content-Length"];
[request setValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"];
[request setHTTPBody:body];
然後伺服器接收body 二進制流
『叄』 iOS大文件的分片上傳和斷點上傳
由於最近比較忙,所以一直沒有寫博客,現在分享一些大文件的上傳的問題!斷點續傳和分片上傳。因為文件過大(比如1G以上),必須要考慮上傳過程網路中斷的情況。http的網路請求中本身就已經具備了分片上傳功能,當傳輸的文件比較大時,http協議自動會將文件切片(分塊),但這不是我們現在說的重點,我們要做的事是保證在網路中斷後1G的文件已上傳的那部分在下次網路連接時不必再重傳。所以我們本地在上傳的時候,要將大文件進行分片,比如分成1024*1024B,即將大文件分成1M的片進行上傳,伺服器在接收後,再將這些片合並成原始文件,這就是分片的基本原理。斷點續傳要求本地要記錄每一片的上傳的狀態,我通過三個狀態進行了標記(wait loading finish),當網路中斷,再次連接後,從斷點處進行上傳。伺服器通過文件名、總片數判斷該文件是否已全部上傳完成。
下面來說細節:
1、首先獲取文件(音視頻、圖片)
分兩種情況,一種是在相冊庫里直接獲取,一種是調用相機。如果是通過UIImagePickerView來獲取(細節不詳述,網上一大堆),我們會發現當你選定一個視頻的時候,會出現圖1的壓縮頁面,最後我們的app獲取的視頻就是這個經過壓縮後的視頻(不是視頻庫里的原始視頻,這里有個注意點,操作完該壓縮視頻後記得釋放,系統不會幫你釋放的,需要你手動來操作,下面會說到),然後通過UIImagePickerView的協議方法中的- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info獲取視頻的Info
fileInfo = {
= "public.movie";
= "file:///private/var/mobile/Containers/Data/Application/2AAE9E44-0E6D-4499-9AC3-93D44D8342EA/tmp/trim.F36EC46C-4219-43C8-96A7-FA7141AB64D2.MOV";
= "assets-library://asset/asset.MOV?id=DEDA9406-3223-4F87-ABB2-98FB5F5EB9C4&ext=MOV";
}
是選取文件的類型,如KUTTypeImage,KUTTypeMovie。這里注意一下movie和video的區別,一個是有聲音的視頻文件,一個是沒有聲音的視頻文件,當然還有Audio是只有聲音沒有視頻。是視頻的URL(如果是相機拍攝的,那麼這個就是原始拍攝得到的視頻;如果是在相冊庫里選擇的,那就是壓縮之後生成的視頻),注意這個URL不指向相冊庫,通過這個URL你可以操作這個視頻如刪除,拷貝等,可以獲取壓縮後的視頻的大小。是一個指向相冊的URL,官方的解釋是an NSURL that references an asset in the AssetsLibrary framework,通過這個URL,你可以獲取視頻的所有信息,包括文件名,縮略圖,時長等(通過ALAssetsLibrary里的assetsLibraryassetForURL:referenceURLresultBlock:)。
如果是相機拍攝的,注意兩個保存方法:圖片保存到相冊:UIImageJPEGRepresentation([infovalueForKey:],(CGFloat)1.0)metadata:nilcompletionBlock: failureBlock:
高保真壓縮圖片的方法NSData * UIImageJPEGRepresentation ( UIImage *image, CGFloat compressionQuality)
視頻保存到相冊::MediaURL completionBlock:failureBlock:
到這里,我們就獲取了所有需要的文件以及文件信息。下面要做的就是將文件分片。
2、將獲取到的文件分片
首先,我將獲取到的文件保存在這這樣一個類中
@interface CNFile :NSObject
@property(nonatomic,)NSString* fileType;//image or movie
@property(nonatomic,)NSString* filePath;//文件在app中路徑
@property(nonatomic,)NSString* fileName;//文件名
@property(nonatomic,assign)NSIntegerfileSize;//文件大小
@property (nonatomic,assign)NSIntegertrunks;//總片數
@property(nonatomic,)NSString* fileInfo;
@property(nonatomic,strong)UIImage* fileImage;//文件縮略圖
@property(nonatomic,strong) NSMutableArray* fileArr;//標記每片的上傳狀態
@end
這樣我們就可以對每一個CNFile對象進行操作了。
-(void)readDataWithChunk:(NSInteger)chunk file:(CNFile*)file{
總片數的獲取方法:
intoffset =1024*1024;(每一片的大小是1M)
NSIntegerchunks = (file.fileSize%1024==0)?((int)(file.fileSize/1024*1024)):((int)(file.fileSize/(1024*1024) +1));
NSLog(@"chunks = %ld",(long)chunks);
將文件分片,讀取每一片的數據:
NSData* data;
NSFileHandle*readHandle = [:file.filePath];
[readHandleseekToFileOffset:offset * chunk];
data = [readHandlereadDataOfLength:offset];
}
這樣我們就獲取了每一片要上傳的數據,然後詢問伺服器,該片是否已經存在
(方法-(void)ifHaveData:(NSData*)data WithChunk:(NSInteger)chunk file:(CNFile*)file)
,如果存在,令chunk+1,重復上面的方法讀取下一片,直到伺服器不存在該片,那麼上傳該片數據。在這個方法中注意設置該chunk的上傳狀態(wait loading finish),這將關繫到本地判斷該文件是否已全部上傳完成。
下一步就是上傳的過程:
-(void)uploadData:(NSData*) data WithChunk:(NSInteger) chunk file:(CNFile*)file;
在伺服器返回該片上傳成功後,我們要做的事有很多:
1)先將已經成功上傳的本片的flag置finish
[file.fileArrreplaceObjectAtIndex:chunk withObject:@「finish"];
2)查看是否所有片的flag都已經置finish,如果都已經finishi,說明該文件上傳完成,那麼刪除該文件,上傳下一個文件或者結束。
for(NSIntegerj =0; j
if(j == chunks || ((j == chunks -1)&&([file.fileArr[j]isEqualToString:@"finish"])))
[medeleteFile:file.filePath];
[mereadNextFile];
}
3)如果沒有都finish,那麼看本地下一chunk對用的flag是否是wait
NSLog(@"查看第%ld片的狀態",chunk+1);
for(NSIntegeri = chunk+1;i < chunks;i++)
{
NSString* flag = [file.fileArrobjectAtIndex:i];
if([flagisEqualToString:@"wait"]) {
[mereadDataWithChunk:ifileName:fileNamefile:file];
break;
}
}
在第2、3步之間可以有一個 2.5)判斷是否暫停上傳
if(me.isPause ==YES)
{
//將目前讀到了第幾個文件的第幾片保存到本地
[selfsaveProgressWithChunk:chunk file:file];
return;
}
這個操作實際上和上傳過程中斷網是一樣的,為了斷點續傳,在斷網或者暫停的時候,我們要將目前的進度保存起來,以便下次上傳時略過前面已置finish的片。
然後還有一個問題,如果我們就這樣線性的一片一片上傳,實際上失去了分片上傳的意義,應該結合多線程,使分片上傳過程並發執行,同時上傳多片,這樣就提高了上傳效率,並充分利用了網路帶寬。
dispatch_async(dispatch_queue_t queue, ^{
[mereadDataWithChunk: chunk];
})
最後注意一下,每上傳完一個視頻,去設置里看看你的app佔用的存儲空間有沒有增大哦,如果你沒有處理那個生成的壓縮視頻,你會發現你的app的空間佔用量是很大的。
站在大牛的肩膀上開發。
『肆』 怎樣只復制目錄結構,不復制文件
拷貝指定大小的文件
#import "SizeCopy.h"
@implementation SizeCopy
-(void)accordingByteCopy:(NSInteger)size
{
//獲取桌面上的文件路徑,以便拷貝
NSString *oldPath=@"/Users/scjy/Desktop/video.mp4";
//指定將要拷貝到哪裡
NSString *newPath=@"/Users/scjy/Desktop/movie.mp4";
//創建文件管理者,准備創建文件
NSFileManager *fileManager=[NSFileManager defaultManager];
//判斷將要創建的文件是不是已經存在
BOOL isHave=[fileManager fileExistsAtPath:newPath];
if (!isHave) {
//不存在的話,開始執行創建,並判斷是不是創建成功
BOOL isSec=[fileManager createFileAtPath:newPath contents:nil attributes:nil];
if (isSec) {
NSLog(@"文件創建成功,開始復制");
}
else
{
return;
}
}
NSFileHandle *oldHandle=[NSFileHandle fileHandleForReadingAtPath:oldPath];//讀取文件的handle
NSFileHandle *newHandle=[NSFileHandle fileHandleForUpdatingAtPath:newPath];//寫入文件的handle
//表示文件已經讀取過,指針已經移動到數據的最後一位
//NSLog(@"%ld",[oldHandle availableData].length);--有準確值
//NSLog(@"%ld",[oldHandle availableData].length);--值為0
//attributesOfItemAtPath獲取文件的大小,內容等方法
NSDictionary *dictionary=[fileManager attributesOfItemAtPath:oldPath error:nil];
//返迴文件的有效內容大小
NSNumber *lenNum=[dictionary valueForKey:NSFileSize];
NSInteger fileLength=[lenNum integerValue];//轉成基本數據類型
NSInteger readLength=0;
BOOL isEnd=NO;
while (!isEnd) {
NSData *data=nil;
//獲取剩餘未拷貝文件長度
NSInteger subLegth=fileLength-readLength;
//判斷是不是最後一次節點復制文件
if (subLegth<size) {
isEnd=YES;
[oldHandle readDataToEndOfFile];//讀取到文件末尾
NSLog(@"拷貝完成:100%@",@"%");
}
else
{
data=[oldHandle readDataOfLength:size];//讀取若干位元組
readLength+=size;
//跳轉到拷貝結束的文件節點
[oldHandle seekToFileOffset:readLength];
//計算拷貝比例
NSNumber *readNum=[NSNumber numberWithInteger:readLength];
NSLog(@"正在拷貝:%.3f%@",[readNum doubleValue]/[lenNum doubleValue]*100,@"%");
}
[newHandle writeData:data];//寫入文件
}
[oldHandle closeFile];//關閉文件
[newHandle closeFile];
}
@end
轉載〈樹下茶壺〉
有點煩鎖……
應該可以用簡單一點的批處理完成。