// 上传本地文件,或者删除服务器端文件 // parameters: // strStyle 当包含 delete 的时候,表示要删除 strFilePath 所指的文件 // return: // -2 时间戳不匹配 // -1 一般性错误 // 0 成功 // 其他 成功删除的文件和目录个数 public int WriteFile( string strRootPath, string strFilePath, string strRanges, long lTotalLength, byte[] baSource, string strStyle, byte[] baInputTimestamp, out byte[] baOutputTimestamp, out string strError) { baOutputTimestamp = null; strError = ""; if (String.IsNullOrEmpty(strFilePath) == true) { strError = "strFilePath 参数值不能为空"; return(-1); } if (lTotalLength < 0) { strError = "lTotalLength 参数值不能为负数"; return(-1); } if (strStyle == null) { strStyle = ""; } bool bDelete = StringUtil.IsInList("delete", strStyle) == true; if (bDelete == true) { int nDeleteCount = 0; string strDirectory = Path.GetDirectoryName(strFilePath); string strPattern = Path.GetFileName(strFilePath); DirectoryInfo di = new DirectoryInfo(strDirectory); FileSystemInfo[] sis = di.GetFileSystemInfos(strPattern); foreach (FileSystemInfo si in sis) { // 安全性检查:不允许文件和目录越出指定的根目录 if (PathUtil.IsChildOrEqual(si.FullName, strRootPath) == false) { continue; } if (si is DirectoryInfo) { // 删除一个目录 _physicalFileCache.ClearAll(); PathUtil.DeleteDirectory(si.FullName); nDeleteCount++; continue; } if (si is FileInfo) { // 删除一个文件 //if (File.Exists(si.FullName) == true) // File.Delete(si.FullName); _physicalFileCache.FileDeleteIfExists(si.FullName); string strNewFilePath1 = GetNewFileName(si.FullName); //if (File.Exists(strNewFilePath1) == true) // File.Delete(strNewFilePath1); _physicalFileCache.FileDeleteIfExists(strNewFilePath1); string strRangeFileName = GetRangeFileName(si.FullName); //if (File.Exists(strRangeFileName) == true) // File.Delete(strRangeFileName); _physicalFileCache.FileDeleteIfExists(strRangeFileName); nDeleteCount++; } } return(nDeleteCount); } #if NO if (bDelete == true && Directory.Exists(strFilePath) == true) { // 删除一个目录 PathUtil.DeleteDirectory(strFilePath); return(0); } string strNewFilePath = GetNewFileName(strFilePath); if (bDelete == true && File.Exists(strFilePath) == true) { // 删除一个文件 if (File.Exists(strFilePath) == true) { File.Delete(strFilePath); } if (File.Exists(strNewFilePath) == true) { File.Delete(strNewFilePath); } string strRangeFileName = GetRangeFileName(strFilePath); if (File.Exists(strRangeFileName) == true) { File.Delete(strRangeFileName); } return(0); } #endif if (bDelete == false && baSource == null) { strError = "baSource 参数值不能为 null"; return(-1); } string strNewFilePath = GetNewFileName(strFilePath); // 确保文件的路径所经过的所有子目录已经创建 PathUtil.TryCreateDir(Path.GetDirectoryName(strFilePath)); //************************************************* // 检查时间戳,当目标文件存在时 if (File.Exists(strFilePath) == true || File.Exists(strNewFilePath) == true) { if (StringUtil.IsInList("ignorechecktimestamp", strStyle) == false) { if (File.Exists(strNewFilePath) == true) { baOutputTimestamp = FileUtil.GetFileTimestamp(strNewFilePath); } else { baOutputTimestamp = FileUtil.GetFileTimestamp(strFilePath); } if (ByteArray.Compare(baOutputTimestamp, baInputTimestamp) != 0) { strError = "时间戳不匹配"; return(-2); } } } else { if (bDelete == true) { string strRangeFileName = GetRangeFileName(strFilePath); //if (File.Exists(strRangeFileName) == true) // File.Delete(strRangeFileName); _physicalFileCache.FileDeleteIfExists(strRangeFileName); return(0); } // 创建空文件 _physicalFileCache.ClearItems(strFilePath); using (FileStream s = File.Create(strFilePath)) { } baOutputTimestamp = FileUtil.GetFileTimestamp(strFilePath); } #if NO // 删除文件 if (bDelete == true) { if (File.Exists(strFilePath) == true) { File.Delete(strFilePath); } if (File.Exists(strNewFilePath) == true) { File.Delete(strNewFilePath); } string strRangeFileName = GetRangeFileName(strFilePath); if (File.Exists(strRangeFileName) == true) { File.Delete(strRangeFileName); } return(0); } #endif //************************************************** long lCurrentLength = 0; { if (baSource.Length == 0) { if (strRanges != "") { strError = "当 baSource 参数的长度为 0 时,strRanges 的值却为 '" + strRanges + "',不匹配,此时 strRanges 的值应为空字符串"; return(-1); } // 把写到 metadata 里的尺寸设好 FileInfo fi = new FileInfo(strFilePath); lCurrentLength = fi.Length; fi = null; } } //****************************************** // 写数据 if (string.IsNullOrEmpty(strRanges) == true) { if (lTotalLength > 0) { strRanges = "0-" + Convert.ToString(lTotalLength - 1); } else { strRanges = ""; } } string strRealRanges = strRanges; // 检查本次传来的范围是否是完整的文件。 bool bIsComplete = false; if (lTotalLength == 0) { bIsComplete = true; } else { // -1 出错 // 0 还有未覆盖的部分 // 1 本次已经完全覆盖 int nState = RangeList.MergeContentRangeString(strRanges, "", lTotalLength, out strRealRanges, out strError); if (nState == -1) { strError = "MergeContentRangeString() error 1 : " + strError + " (strRanges='" + strRanges + "' lTotalLength=" + lTotalLength.ToString() + ")"; return(-1); } if (nState == 1) { bIsComplete = true; } } if (bIsComplete == true) { if (baSource.Length != lTotalLength) { strError = "范围 '" + strRanges + "' 与数据字节数组长度 '" + baSource.Length.ToString() + "' 不符合"; return(-1); } } RangeList rangeList = new RangeList(strRealRanges); #if NO // 开始写数据 Stream target = null; if (bIsComplete == true) { target = File.Create(strFilePath); //一次性发完,直接写到文件 } else { target = File.Open(strNewFilePath, FileMode.OpenOrCreate); } try { int nStartOfBuffer = 0; for (int i = 0; i < rangeList.Count; i++) { RangeItem range = (RangeItem)rangeList[i]; // int nStartOfTarget = (int)range.lStart; int nLength = (int)range.lLength; if (nLength == 0) { continue; } Debug.Assert(range.lStart >= 0, ""); // 移动目标流的指针到指定位置 target.Seek(range.lStart, SeekOrigin.Begin); target.Write(baSource, nStartOfBuffer, nLength); nStartOfBuffer += nLength; } } finally { target.Close(); } #endif // 开始写数据 StreamItem target = null; if (bIsComplete == true) { target = _physicalFileCache.GetStream(strFilePath, FileMode.Create, FileAccess.Write, false); //一次性发完,直接写到文件 } else { target = _physicalFileCache.GetStream(strNewFilePath, FileMode.OpenOrCreate, FileAccess.Write); } try { int nStartOfBuffer = 0; for (int i = 0; i < rangeList.Count; i++) { RangeItem range = (RangeItem)rangeList[i]; // int nStartOfTarget = (int)range.lStart; int nLength = (int)range.lLength; if (nLength == 0) { continue; } Debug.Assert(range.lStart >= 0, ""); // 移动目标流的指针到指定位置 target.FileStream.FastSeek(range.lStart); target.FileStream.Write(baSource, nStartOfBuffer, nLength); nStartOfBuffer += nLength; } } finally { _physicalFileCache.ReturnStream(target); } { string strRangeFileName = GetRangeFileName(strFilePath); // 如果一次性写满的情况,需要做下列几件事情: // 1.时间戳以目标文件计算 // 2.写到metadata的长度为目标文件总长度 // 3.如果存在临时辅助文件,则删除这些文件。 // 4. 设置目标文件的 LastWriteTime if (bIsComplete == true) { // baOutputTimestamp = CreateTimestampForCfg(strFilePath); lCurrentLength = lTotalLength; // 删除辅助文件 //if (File.Exists(strNewFilePath) == true) // File.Delete(strNewFilePath); _physicalFileCache.FileDeleteIfExists(strNewFilePath); //if (File.Exists(strRangeFileName) == true) // File.Delete(strRangeFileName); _physicalFileCache.FileDeleteIfExists(strRangeFileName); goto END1; } //**************************************** //处理辅助文件 bool bEndWrite = false; // 是否为最后一次写入操作 string strResultRange = ""; if (strRanges == "" || strRanges == null) { bEndWrite = true; } else { string strOldRanges = ""; if (IsFirstRange(strRanges, lTotalLength, out bEndWrite) == false) { if (File.Exists(strRangeFileName) == true) { string strText = FileUtil.File2StringE(strRangeFileName); string strOldTotalLength = ""; StringUtil.ParseTwoPart(strText, "|", out strOldRanges, out strOldTotalLength); } // return // -1 出错 // 0 还有未覆盖的部分 // 1 本次已经完全覆盖 int nState1 = RangeList.MergeContentRangeString(strRanges, strOldRanges, lTotalLength, out strResultRange, out strError); if (nState1 == -1) { strError = "MergeContentRangeString() error 2 : " + strError + " (strRanges='" + strRanges + "' strOldRanges='" + strOldRanges + "' ) lTotalLength=" + lTotalLength.ToString() + ""; return(-1); } if (nState1 == 1) { bEndWrite = true; } } else { strResultRange = strRanges; } } // 如果文件已满,需要做下列几件事情: // 1.按最大长度截临时文件 // 2.将临时文件拷到目标文件 // 3.删除new,range辅助文件 // 4.时间戳以目标文件计算 // 5.metadata的长度为目标文件的总长度 // 6. 设置目标文件的 LastWriteTime if (bEndWrite == true) { _physicalFileCache.ClearItems(strNewFilePath); using (Stream s = new FileStream(strNewFilePath, FileMode.OpenOrCreate)) { s.SetLength(lTotalLength); } // TODO: Move 文件较好。改名 //File.Delete(strFilePath); //File.Move(strNewFilePath, strFilePath); this._physicalFileCache.FileDelete(strFilePath); this._physicalFileCache.FileMove(strNewFilePath, strFilePath); //if (File.Exists(strRangeFileName) == true) // File.Delete(strRangeFileName); _physicalFileCache.FileDeleteIfExists(strRangeFileName); baOutputTimestamp = FileUtil.GetFileTimestamp(strFilePath); lCurrentLength = lTotalLength; bIsComplete = true; } else { //如果文件未满,需要做下列几件事情: // 1.把目前的range写到range辅助文件 // 2.时间戳以临时文件计算 // 3.metadata的长度为-1,即未知的情况 FileUtil.String2File(strResultRange + "|" + lTotalLength.ToString(), strRangeFileName); lCurrentLength = -1; baOutputTimestamp = FileUtil.GetFileTimestamp(strNewFilePath); } } END1: if (bIsComplete == true) { // 多轮上传的内容完成后,最后需要单独设置文件最后修改时间 string strLastWriteTime = StringUtil.GetStyleParam(strStyle, "last_write_time"); // parameters: // baTimeStamp 8 byte 的表示 ticks 的文件最后修改时间。应该是 GMT 时间 FileUtil.SetFileLastWriteTimeByTimestamp(strFilePath, ByteArray.GetTimeStampByteArray(strLastWriteTime)); baOutputTimestamp = FileUtil.GetFileTimestamp(strFilePath); // 结束时自动展开一个压缩文件 if (StringUtil.IsInList("extractzip", strStyle) == true) { try { ReadOptions option = new ReadOptions(); option.Encoding = Encoding.UTF8; _physicalFileCache.ClearItems(strFilePath); using (ZipFile zip = ZipFile.Read(strFilePath, option)) { foreach (ZipEntry e in zip) { string strTargetDir = Path.GetDirectoryName(strFilePath); e.Extract(strTargetDir, ExtractExistingFileAction.OverwriteSilently); // 2017/4/8 修正文件最后修改时间 string strFullPath = Path.Combine(strTargetDir, e.FileName); if ((e.Attributes & FileAttributes.Directory) == 0) { if (e.LastModified != File.GetLastWriteTime(strFullPath)) { // 时间有可能不一致,可能是夏令时之类的问题 File.SetLastWriteTime(strFullPath, e.LastModified); } Debug.Assert(e.LastModified == File.GetLastWriteTime(strFullPath)); } } } File.Delete(strFilePath); } catch (Exception ex) { strError = ExceptionUtil.GetAutoText(ex); return(-1); } } } return(0); }