0

ユーザーがアップロードしたファイルをファイルシステムに保存するための最良の方法を見つけようとしています。ファイルの範囲は、個人ファイルからwikiファイルまでです。もちろん、DBは私がまだ理解していない何らかの方法でそれらのファイルを指し示します。

基本要件:

  • 人々がファイル名を推測できないようにかなりまともなセキュリティ(Picture001.jpg、Picture002.jpg、Music001.mp3は大したことではありません)
  • 簡単にバックアップとミラーリングが可能(バックアップするたびにHDD全体をコピーする必要がない方法が好きです。最新のアイテムだけをバックアップするというアイデアは好きですが、ここでのオプションには柔軟性があります。 )。
  • 必要に応じて、複数のサーバー上の数百万のファイルに拡張可能。
4

5 に答える 5

3

1つの手法は、コンテンツのハッシュ(SHA1)にちなんで名付けられたファイルにデータを保存することです。これは簡単に推測することはできず、どのバックアッププログラムでも処理できるはずであり、簡単にシャーディングされます(1台のマシンに0から始まるハッシュ、次のマシンに1から始まるハッシュなどを格納することによって)。

データベースには、ユーザーに割り当てられた名前とコンテンツのSHA1ハッシュの間のマッピングが含まれます。

于 2008-10-22T10:04:32.983 に答える
3

ファイル名のガイド。フォルダー階層を自動的に展開し、各フォルダーに数千のファイル/フォルダーしかありません。新しいファイルのバックアップは、新しいフォルダーのバックアップによって行われます。

使用している環境やプログラミング言語を示していませんが、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;
                }
            }
        }
    }
}
于 2008-10-22T10:11:04.220 に答える
1

ファイル名のSHA1ハッシュ+ソルト(または、必要に応じて、ファイルの内容。これにより、重複ファイルの検出が容易になりますが、サーバーにより多くのストレスがかかります)。これには、一意にするための微調整が必​​要になる場合があります(つまり、アップロードされたユーザーIDまたはタイムスタンプを追加します)。

フォルダー構造は、ハッシュの一部によって決まります。

たとえば、ハッシュが「2fd4e1c67a2d28fced849ee1bb76e7391b93eb12」の場合、フォルダーは次のようになります。

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

これは、大きなフォルダを防ぐためです(一部のオペレーティングシステムでは、数百万のファイルを含むフォルダを列挙するのに問題があるため、ハッシュの一部にいくつかのサブフォルダを作成します。レベル数は、予想されるファイル数によって異なりますが、通常は2つまたは3つです。適正。

于 2008-10-22T10:05:14.480 に答える
0

Phill Sacreの答えを拡張すると、セキュリティのもう1つの側面は、アップロードされたファイルに別のドメイン名を使用し(インスタントの場合、ウィキペディアはupload.wikimedia.orgを使用します)、ドメインがサイトのCookieを読み取れないようにすることです。これにより、ユーザーのセッションCookieを盗むためのスクリプトを含むHTMLファイルをアップロードできなくなります(一部のブラウザはそれを無視してファイルの内容に基づいて推測するため、Content-Typeヘッダーを設定するだけでは不十分です。他の種類のファイルに埋め込まれているため、HTMLをチェックして許可しないのは簡単ではありません)。

于 2008-10-22T10:54:25.910 に答える
0

質問の1つの側面(セキュリティ)に関して:アップロードされたファイルをファイルシステムに安全に保存する最良の方法は、アップロードされたファイルがWebルートの外にあることを確認することです(つまり、URLを介して直接アクセスすることはできません-あなたスクリプトを実行する必要があります)。

これにより、ユーザーが何をダウンロードできるか (セキュリティ) を完全に制御し、ログなどを許可できます。もちろん、スクリプト自体が安全であることを確認する必要がありますが、許可した人だけが特定のファイルをダウンロードできることを意味します。

于 2008-10-22T10:06:46.490 に答える