Filesystems 如何将上载的文件存储在文件系统中?

Filesystems 如何将上载的文件存储在文件系统中?,filesystems,organization,Filesystems,Organization,我正试图找出将用户上传的文件存储在文件系统中的最佳方法。这些文件的范围从个人文件到维基文件。当然,DB会以某种方式指向那些我还没有弄明白的文件 基本要求: 仙女体面的安全,所以人们不能猜测文件名 (图片001.jpg,图片002.jpg, Music001.mp3是一个大禁忌) 易于备份和镜像(我更喜欢一种方式,这样我就不必每次备份时都复制整个HDD。我喜欢只备份最新项目的想法,但我在这里的选项很灵活。) 如果需要,可扩展到多台服务器上的数百万个文件 一种技术是将数据存储在以其内容的哈希(SH

我正试图找出将用户上传的文件存储在文件系统中的最佳方法。这些文件的范围从个人文件到维基文件。当然,DB会以某种方式指向那些我还没有弄明白的文件

基本要求:

  • 仙女体面的安全,所以人们不能猜测文件名 (图片001.jpg,图片002.jpg, Music001.mp3是一个大禁忌)
  • 易于备份和镜像(我更喜欢一种方式,这样我就不必每次备份时都复制整个HDD。我喜欢只备份最新项目的想法,但我在这里的选项很灵活。)
  • 如果需要,可扩展到多台服务器上的数百万个文件

    • 一种技术是将数据存储在以其内容的哈希(SHA1)命名的文件中。这是不容易猜测的,任何备份程序都应该能够处理它,并且它很容易被切分(通过在一台机器上存储以0开头的哈希,在下一台机器上存储以1开头的哈希,等等)


      数据库将包含用户指定的名称和内容的SHA1散列之间的映射。

      SHA1散列文件名+一个salt(或者,如果需要,文件内容的SHA1散列)。这使得检测重复文件更容易,但也会给服务器带来更大的压力。这可能需要一些调整以使其唯一(即添加上传的用户ID或时间戳),salt是为了使其不可猜测

      文件夹结构然后由散列的一部分组成

      例如,如果哈希为“2fd4e1c67a2d28fced849ee1bb76e7391b93eb12”,则文件夹可以是:

      /2
      /2/2f/
      /2/2f/2fd/
      /2/2f/2fd/2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
      

      这是为了防止大型文件夹(某些操作系统在枚举包含一百万个文件的文件夹时遇到问题,因此会为散列的一部分创建一些子文件夹。多少级别?这取决于您期望的文件数量,但2或3通常是合理的。

      仅就您问题的一个方面而言(安全性):将上传的文件安全存储在文件系统中的最佳方法是确保上传的文件不在webroot中(即,您不能通过URL直接访问它们-您必须通过脚本)


      这使您可以完全控制人们可以下载的内容(安全性)当然,你必须确保脚本本身是安全的,但这意味着只有你允许的人才能下载某些文件。

      文件名的GUID,自动扩展文件夹层次结构,每个文件夹中不超过几千个文件/文件夹。备份新文件由ba完成整理新文件夹

      您尚未指明您使用的环境和/或编程语言,但这里有一个C#/.net/Windows示例:

      using System;
      using System.IO;
      using System.Xml.Serialization;
      
      /// <summary>
      /// Class for generating storage structure and file names for document storage.
      /// Copyright (c) 2008, Huagati Systems Co.,Ltd. 
      /// </summary>
      
      public class DocumentStorage
      {
          private static StorageDirectory _StorageDirectory = null;
      
          public static string GetNewUNCPath()
          {
              string storageDirectory = GetStorageDirectory();
              if (!storageDirectory.EndsWith("\\"))
              {
                  storageDirectory += "\\";
              }
              return storageDirectory + GuidEx.NewSeqGuid().ToString() + ".data";
          }
      
          public static void SaveDocumentInfo(string documentPath, Document documentInfo)
          {
              //the filestream object don't like NTFS streams so this is disabled for now...
              return;
      
              //stores a document object in a separate "docinfo" stream attached to the file it belongs to
              //XmlSerializer ser = new XmlSerializer(typeof(Document));
              //string infoStream = documentPath + ":docinfo";
              //FileStream fs = new FileStream(infoStream, FileMode.Create);
              //ser.Serialize(fs, documentInfo);
              //fs.Flush();
              //fs.Close();
          }
      
          private static string GetStorageDirectory()
          {
              string storageRoot = ConfigSettings.DocumentStorageRoot;
              if (!storageRoot.EndsWith("\\"))
              {
                  storageRoot += "\\";
              }
      
              //get storage directory if not set
              if (_StorageDirectory == null)
              {
                  _StorageDirectory = new StorageDirectory();
                  lock (_StorageDirectory)
                  {
                      string path = ConfigSettings.ReadSettingString("CurrentDocumentStoragePath");
                      if (path == null)
                      {
                          //no storage tree created yet, create first set of subfolders
                          path = CreateStorageDirectory(storageRoot, 1);
                          _StorageDirectory.FullPath = path.Substring(storageRoot.Length);
                          ConfigSettings.WriteSettingString("CurrentDocumentStoragePath", _StorageDirectory.FullPath);
                      }
                      else
                      {
                          _StorageDirectory.FullPath = path;
                      }
                  }
              }
      
              int fileCount = (new DirectoryInfo(storageRoot + _StorageDirectory.FullPath)).GetFiles().Length;
              if (fileCount > ConfigSettings.FolderContentLimitFiles)
              {
                  //if the directory has exceeded number of files per directory, create a new one...
                  lock (_StorageDirectory)
                  {
                      string path = GetNewStorageFolder(storageRoot + _StorageDirectory.FullPath, ConfigSettings.DocumentStorageDepth);
                      _StorageDirectory.FullPath = path.Substring(storageRoot.Length);
                      ConfigSettings.WriteSettingString("CurrentDocumentStoragePath", _StorageDirectory.FullPath);
                  }
              }
      
              return storageRoot + _StorageDirectory.FullPath;
          }
      
          private static string GetNewStorageFolder(string currentPath, int currentDepth)
          {
              string parentFolder = currentPath.Substring(0, currentPath.LastIndexOf("\\"));
              int parentFolderFolderCount = (new DirectoryInfo(parentFolder)).GetDirectories().Length;
              if (parentFolderFolderCount < ConfigSettings.FolderContentLimitFolders)
              {
                  return CreateStorageDirectory(parentFolder, currentDepth);
              }
              else
              {
                  return GetNewStorageFolder(parentFolder, currentDepth - 1);
              }
          }
      
          private static string CreateStorageDirectory(string currentDir, int currentDepth)
          {
              string storageDirectory = null;
              string directoryName = GuidEx.NewSeqGuid().ToString();
              if (!currentDir.EndsWith("\\"))
              {
                  currentDir += "\\";
              }
              Directory.CreateDirectory(currentDir + directoryName);
      
              if (currentDepth < ConfigSettings.DocumentStorageDepth)
              {
                  storageDirectory = CreateStorageDirectory(currentDir + directoryName, currentDepth + 1);
              }
              else
              {
                  storageDirectory = currentDir + directoryName;
              }
              return storageDirectory;
          }
      
          private class StorageDirectory
          {
              public string DirectoryName { get; set; }
              public StorageDirectory ParentDirectory { get; set; }
              public string FullPath
              {
                  get
                  {
                      if (ParentDirectory != null)
                      {
                          return ParentDirectory.FullPath + "\\" + DirectoryName;
                      }
                      else
                      {
                          return DirectoryName;
                      }
                  }
                  set
                  {
                      if (value.Contains("\\"))
                      {
                          DirectoryName = value.Substring(value.LastIndexOf("\\") + 1);
                          ParentDirectory = new StorageDirectory { FullPath = value.Substring(0, value.LastIndexOf("\\")) };
                      }
                      else
                      {
                          DirectoryName = value;
                      }
                  }
              }
          }
      }
      
      使用系统;
      使用System.IO;
      使用System.Xml.Serialization;
      /// 
      ///类,用于生成文档存储的存储结构和文件名。
      ///版权所有(c)2008,华加蒂系统有限公司。
      /// 
      公共类文档存储
      {
      私有静态存储目录_StorageDirectory=null;
      公共静态字符串GetNewUNCPath()
      {
      字符串storageDirectory=GetStorageDirectory();
      如果(!storageDirectory.EndsWith(“\\”)
      {
      storageDirectory+=“\\”;
      }
      返回storageDirectory+GuidEx.NewSeqGuid().ToString()+“.data”;
      }
      公共静态void SaveDocumentInfo(字符串documentPath,文档documentInfo)
      {
      //filestream对象不喜欢NTFS流,因此目前已禁用此功能。。。
      返回;
      //将文档对象存储在附加到其所属文件的单独“docinfo”流中
      //XmlSerializer ser=新的XmlSerializer(typeof(Document));
      //字符串infoStream=documentPath+“:docinfo”;
      //FileStream fs=newfilestream(infoStream,FileMode.Create);
      //序列序列化(fs、documentInfo);
      //fs.Flush();
      //fs.Close();
      }
      私有静态字符串GetStorageDirectory()
      {
      字符串storageRoot=ConfigSettings.DocumentStorageRoot;
      如果(!storageRoot.EndsWith(“\\”)
      {
      storageRoot+=“\\”;
      }
      //如果未设置,则获取存储目录
      如果(_StorageDirectory==null)
      {
      _StorageDirectory=新的StorageDirectory();
      锁(_StorageDirectory)
      {
      字符串路径=ConfigSettings.ReadSettingString(“CurrentDocumentStoragePath”);
      if(路径==null)
      {
      //尚未创建存储树,请创建第一组子文件夹
      path=CreateStorageDirectory(storageRoot,1);
      _StorageDirectory.FullPath=path.Substring(storageRoot.Length);
      ConfigSettings.WriteSettingString(“CurrentDocumentStoragePath”,\u StorageDirectory.FullPath);
      }
      其他的
      {
      _StorageDirectory.FullPath=path;
      }
      }
      }
      int fileCount=(新目录信息(storageRoot+_StorageDirectory.FullPath)).GetFiles().Length;
      如果(fileCount>ConfigSettings.FolderContentLimitFiles)
      {
      //如果目录已超过每个目录的文件数,请创建一个新目录。。。
      锁(_StorageDirectory)
      {
      字符串路径=GetNewsStorageFolder(storageRoot+\u StorageDirectory.FullPath,ConfigSettings.DocumentStorageDepth);
      _StorageDirectory.FullPath=path.Substring(storageRoot.Length);
      ConfigSettings.WriteSettingString(“CurrentDocumentStoragePath”,\u StorageDirectory.FullPath);
      }
      }
      返回storageRoot+\u StorageDirectory.FullPath;
      }
      私有静态字符串GetNewsStorage文件夹(字符串currentPath,int currentDepth)
      {
      字符串parentFolder=currentPath.Substring(0,currentPath.LastIndexOf(“\\”);
      int parentfoldercount=(新目录信息(parentFolder)).GetDirectories().Length;
      如果(parentFolderCount