├── .gitignore ├── Extrator ├── Extrator.csproj ├── Program.cs └── Properties │ └── AssemblyInfo.cs ├── FileDB.sln ├── FileDB ├── Engine │ └── Engine.cs ├── Exceptions │ └── FileDBException.cs ├── Factories │ ├── DataFactory.cs │ ├── FileFactory.cs │ ├── HeaderFactory.cs │ ├── IndexFactory.cs │ └── PageFactory.cs ├── FileDB.cs ├── FileDB.csproj ├── Helper │ ├── BinaryReaderExtensions.cs │ ├── BinaryWriterExtensions.cs │ ├── CacheIndexPage.cs │ ├── DebugFile.cs │ ├── FileDBExtensions.cs │ ├── IOExceptionExtensions.cs │ ├── MimeTypeConverter.cs │ ├── Range.cs │ └── StringExtensions.cs ├── Properties │ └── AssemblyInfo.cs ├── Stream │ └── FileDBStream.cs └── Structure │ ├── BasePage.cs │ ├── DataPage.cs │ ├── EntryInfo.cs │ ├── Header.cs │ ├── IndexLink.cs │ ├── IndexNode.cs │ └── IndexPage.cs ├── Icons ├── filedb_200h.png └── logo-filedb.txt ├── LICENSE ├── MvcTest ├── Content │ ├── fileuploader.css │ ├── fileuploader.js │ ├── loading.gif │ └── no-picture.jpg ├── Controllers │ └── FileDBController.cs ├── Global.asax ├── Global.asax.cs ├── MvcTest.csproj ├── Properties │ └── AssemblyInfo.cs ├── Views │ ├── FileDB │ │ ├── Index.cshtml │ │ └── Structure.cshtml │ └── Web.config ├── Web.Debug.config ├── Web.Release.config └── Web.config ├── README.md └── TestConsole ├── Program.cs ├── Properties └── AssemblyInfo.cs └── TestConsole.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | [Rr]eleases/ 14 | x64/ 15 | x86/ 16 | build/ 17 | bld/ 18 | [Bb]in/ 19 | [Oo]bj/ 20 | 21 | # Roslyn cache directories 22 | *.ide/ 23 | 24 | # MSTest test Results 25 | [Tt]est[Rr]esult*/ 26 | [Bb]uild[Ll]og.* 27 | 28 | #NUNIT 29 | *.VisualState.xml 30 | TestResult.xml 31 | 32 | # Build Results of an ATL Project 33 | [Dd]ebugPS/ 34 | [Rr]eleasePS/ 35 | dlldata.c 36 | 37 | *_i.c 38 | *_p.c 39 | *_i.h 40 | *.ilk 41 | *.meta 42 | *.obj 43 | *.pch 44 | *.pdb 45 | *.pgc 46 | *.pgd 47 | *.rsp 48 | *.sbr 49 | *.tlb 50 | *.tli 51 | *.tlh 52 | *.tmp 53 | *.tmp_proj 54 | *.log 55 | *.vspscc 56 | *.vssscc 57 | .builds 58 | *.pidb 59 | *.svclog 60 | *.scc 61 | 62 | # Chutzpah Test files 63 | _Chutzpah* 64 | 65 | # Visual C++ cache files 66 | ipch/ 67 | *.aps 68 | *.ncb 69 | *.opensdf 70 | *.sdf 71 | *.cachefile 72 | 73 | # Visual Studio profiler 74 | *.psess 75 | *.vsp 76 | *.vspx 77 | 78 | # TFS 2012 Local Workspace 79 | $tf/ 80 | 81 | # Guidance Automation Toolkit 82 | *.gpState 83 | 84 | # ReSharper is a .NET coding add-in 85 | _ReSharper*/ 86 | *.[Rr]e[Ss]harper 87 | *.DotSettings.user 88 | 89 | # JustCode is a .NET coding addin-in 90 | .JustCode 91 | 92 | # TeamCity is a build add-in 93 | _TeamCity* 94 | 95 | # DotCover is a Code Coverage Tool 96 | *.dotCover 97 | 98 | # NCrunch 99 | _NCrunch_* 100 | .*crunch*.local.xml 101 | 102 | # MightyMoose 103 | *.mm.* 104 | AutoTest.Net/ 105 | 106 | # Web workbench (sass) 107 | .sass-cache/ 108 | 109 | # Installshield output folder 110 | [Ee]xpress/ 111 | 112 | # DocProject is a documentation generator add-in 113 | DocProject/buildhelp/ 114 | DocProject/Help/*.HxT 115 | DocProject/Help/*.HxC 116 | DocProject/Help/*.hhc 117 | DocProject/Help/*.hhk 118 | DocProject/Help/*.hhp 119 | DocProject/Help/Html2 120 | DocProject/Help/html 121 | 122 | # Click-Once directory 123 | publish/ 124 | 125 | # Publish Web Output 126 | *.[Pp]ublish.xml 127 | *.azurePubxml 128 | # TODO: Comment the next line if you want to checkin your web deploy settings 129 | # but database connection strings (with potential passwords) will be unencrypted 130 | *.pubxml 131 | *.publishproj 132 | 133 | # NuGet Packages 134 | *.nupkg 135 | # The packages folder can be ignored because of Package Restore 136 | **/packages/* 137 | # except build/, which is used as an MSBuild target. 138 | !**/packages/build/ 139 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 140 | #!**/packages/repositories.config 141 | 142 | # Windows Azure Build Output 143 | csx/ 144 | *.build.csdef 145 | 146 | # Windows Store app package directory 147 | AppPackages/ 148 | 149 | # Others 150 | sql/ 151 | *.Cache 152 | ClientBin/ 153 | [Ss]tyle[Cc]op.* 154 | ~$* 155 | *~ 156 | *.dbmdl 157 | *.dbproj.schemaview 158 | *.pfx 159 | *.publishsettings 160 | node_modules/ 161 | 162 | # RIA/Silverlight projects 163 | Generated_Code/ 164 | 165 | # Backup & report files from converting an old project file 166 | # to a newer Visual Studio version. Backup files are not needed, 167 | # because we have git ;-) 168 | _UpgradeReport_Files/ 169 | Backup*/ 170 | UpgradeLog*.XML 171 | UpgradeLog*.htm 172 | 173 | # SQL Server files 174 | *.mdf 175 | *.ldf 176 | 177 | # Business Intelligence projects 178 | *.rdl.data 179 | *.bim.layout 180 | *.bim_*.settings 181 | 182 | # Microsoft Fakes 183 | FakesAssemblies/ 184 | -------------------------------------------------------------------------------- /Extrator/Extrator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5} 9 | Exe 10 | Properties 11 | Extrator 12 | Extrator 13 | v4.0 14 | Client 15 | 512 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | x86 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | FileDB\Engine.cs 48 | 49 | 50 | FileDB\FileDBException.cs 51 | 52 | 53 | FileDB\DataFactory.cs 54 | 55 | 56 | FileDB\FileFactory.cs 57 | 58 | 59 | FileDB\HeaderFactory.cs 60 | 61 | 62 | FileDB\IndexFactory.cs 63 | 64 | 65 | FileDB\PageFactory.cs 66 | 67 | 68 | FileDB\FileDB.cs 69 | 70 | 71 | FileDB\BinaryReaderExtensions.cs 72 | 73 | 74 | FileDB\BinaryWriterExtensions.cs 75 | 76 | 77 | FileDB\CacheIndexPage.cs 78 | 79 | 80 | FileDB\DebugFile.cs 81 | 82 | 83 | FileDB\FileDBExtensions.cs 84 | 85 | 86 | FileDB\IOExceptionExtensions.cs 87 | 88 | 89 | FileDB\MimeTypeConverter.cs 90 | 91 | 92 | FileDB\Range.cs 93 | 94 | 95 | FileDB\StringExtensions.cs 96 | 97 | 98 | FileDB\FileDBStream.cs 99 | 100 | 101 | FileDB\BasePage.cs 102 | 103 | 104 | FileDB\DataPage.cs 105 | 106 | 107 | FileDB\EntryInfo.cs 108 | 109 | 110 | FileDB\Header.cs 111 | 112 | 113 | FileDB\IndexLink.cs 114 | 115 | 116 | FileDB\IndexNode.cs 117 | 118 | 119 | FileDB\IndexPage.cs 120 | 121 | 122 | 123 | 124 | 125 | 132 | -------------------------------------------------------------------------------- /Extrator/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Extrator 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | if (args.Length != 0) 14 | { 15 | Console.WriteLine("Usage: Extrator "); 16 | } 17 | 18 | var file = args[0]; 19 | var dir = Path.Combine(Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file)); 20 | 21 | if (Directory.Exists(dir)) 22 | { 23 | Directory.Delete(dir); 24 | } 25 | Directory.CreateDirectory(dir); 26 | 27 | using (var db = new Numeria.IO.FileDB(file, FileAccess.Read)) 28 | { 29 | var entities = db.ListFiles(); 30 | foreach (var entity in entities) 31 | { 32 | var filename = UniqueFilename(dir, entity.FileName); 33 | Console.WriteLine("Extrating.... " + filename); 34 | db.Read(entity.ID, filename); 35 | } 36 | } 37 | 38 | Console.WriteLine("Done."); 39 | } 40 | 41 | static string UniqueFilename(string dir, string filename) 42 | { 43 | var f = Path.Combine(dir, filename); 44 | var index = 1; 45 | 46 | while (File.Exists(f)) 47 | { 48 | index++; 49 | f = Path.Combine(dir, Path.GetFileNameWithoutExtension(filename) + " (" + index + ")" + Path.GetExtension(filename)); 50 | } 51 | 52 | return f; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Extrator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Extrator")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Extrator")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("16334f16-f83d-4f3b-a266-e0b39ca1f4e2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /FileDB.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDB", "FileDB\FileDB.csproj", "{5F72F6FB-89BC-439F-989A-5B9581A50CC5}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcTest", "MvcTest\MvcTest.csproj", "{2750A99F-347B-4963-9658-14FBDF402500}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "TestConsole\TestConsole.csproj", "{5B7F7322-3699-48E5-B631-A06CD50AFE79}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Extrator", "Extrator\Extrator.csproj", "{B71BB947-B7D5-45EF-9EF5-543A63106FA5}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|Mixed Platforms = Debug|Mixed Platforms 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|Mixed Platforms = Release|Mixed Platforms 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 25 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 26 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Debug|x86.ActiveCfg = Debug|Any CPU 27 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 30 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Release|Mixed Platforms.Build.0 = Release|Any CPU 31 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5}.Release|x86.ActiveCfg = Release|Any CPU 32 | {2750A99F-347B-4963-9658-14FBDF402500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {2750A99F-347B-4963-9658-14FBDF402500}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {2750A99F-347B-4963-9658-14FBDF402500}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 35 | {2750A99F-347B-4963-9658-14FBDF402500}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 36 | {2750A99F-347B-4963-9658-14FBDF402500}.Debug|x86.ActiveCfg = Debug|Any CPU 37 | {2750A99F-347B-4963-9658-14FBDF402500}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {2750A99F-347B-4963-9658-14FBDF402500}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {2750A99F-347B-4963-9658-14FBDF402500}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 40 | {2750A99F-347B-4963-9658-14FBDF402500}.Release|Mixed Platforms.Build.0 = Release|Any CPU 41 | {2750A99F-347B-4963-9658-14FBDF402500}.Release|x86.ActiveCfg = Release|Any CPU 42 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Debug|Any CPU.ActiveCfg = Debug|x86 43 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 44 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Debug|Mixed Platforms.Build.0 = Debug|x86 45 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Debug|x86.ActiveCfg = Debug|x86 46 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Debug|x86.Build.0 = Debug|x86 47 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Release|Any CPU.ActiveCfg = Release|x86 48 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Release|Mixed Platforms.ActiveCfg = Release|x86 49 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Release|Mixed Platforms.Build.0 = Release|x86 50 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Release|x86.ActiveCfg = Release|x86 51 | {5B7F7322-3699-48E5-B631-A06CD50AFE79}.Release|x86.Build.0 = Release|x86 52 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Debug|Any CPU.ActiveCfg = Debug|x86 53 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 54 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Debug|Mixed Platforms.Build.0 = Debug|x86 55 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Debug|x86.ActiveCfg = Debug|x86 56 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Debug|x86.Build.0 = Debug|x86 57 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Release|Any CPU.ActiveCfg = Release|x86 58 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Release|Mixed Platforms.ActiveCfg = Release|x86 59 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Release|Mixed Platforms.Build.0 = Release|x86 60 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Release|x86.ActiveCfg = Release|x86 61 | {B71BB947-B7D5-45EF-9EF5-543A63106FA5}.Release|x86.Build.0 = Release|x86 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | EndGlobal 67 | -------------------------------------------------------------------------------- /FileDB/Engine/Engine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class Engine : IDisposable 10 | { 11 | public BinaryReader Reader { get; private set; } 12 | public BinaryWriter Writer { get; private set; } 13 | public CacheIndexPage CacheIndexPage { get; private set; } // Used for cache index pages. 14 | public Header Header { get; private set; } 15 | 16 | public Engine(FileStream stream) 17 | { 18 | Reader = new BinaryReader(stream); 19 | 20 | if (stream.CanWrite) 21 | { 22 | Writer = new BinaryWriter(stream); 23 | Writer.Lock(Header.LOCKER_POS, 1); 24 | } 25 | 26 | Header = new Header(); 27 | HeaderFactory.ReadFromFile(Header, this.Reader); 28 | 29 | CacheIndexPage = new CacheIndexPage(Reader, Writer, Header.IndexRootPageID); 30 | } 31 | 32 | public IndexPage GetFreeIndexPage() 33 | { 34 | var freeIndexPage = CacheIndexPage.GetPage(Header.FreeIndexPageID); 35 | 36 | // Check if "free page" has no more index to be used 37 | if (freeIndexPage.NodeIndex >= IndexPage.NODES_PER_PAGE - 1) 38 | { 39 | Header.LastPageID++; // Take last page and increase 40 | Header.IsDirty = true; 41 | 42 | var newIndexPage = new IndexPage(Header.LastPageID); // Create a new index page 43 | freeIndexPage.NextPageID = newIndexPage.PageID; // Point older page to the new page 44 | Header.FreeIndexPageID = Header.LastPageID; // Update last index page 45 | 46 | CacheIndexPage.AddPage(freeIndexPage, true); 47 | 48 | return newIndexPage; 49 | } 50 | else 51 | { 52 | // Has more free index on same index page? return them 53 | freeIndexPage.NodeIndex++; // Reserve space 54 | return freeIndexPage; 55 | } 56 | } 57 | 58 | public DataPage GetPageData(uint pageID) 59 | { 60 | if (pageID == Header.LastPageID) // Page does not exists in disk 61 | { 62 | var dataPage = new DataPage(pageID); 63 | return dataPage; 64 | } 65 | else 66 | { 67 | return PageFactory.GetDataPage(pageID, Reader, false); 68 | } 69 | } 70 | 71 | // Implement file physic storage 72 | public void Write(EntryInfo entry, Stream stream) 73 | { 74 | // Take the first index page 75 | IndexNode rootIndexNode = IndexFactory.GetRootIndexNode(this); 76 | 77 | // Search and insert the index 78 | var indexNode = IndexFactory.BinaryInsert(entry, rootIndexNode, this); 79 | 80 | // In this moment, the index are ready and saved. I use to add the file 81 | DataFactory.InsertFile(indexNode, stream, this); 82 | 83 | // Update entry information with file length (I know file length only after read all) 84 | entry.FileLength = indexNode.FileLength; 85 | 86 | // Only after insert all stream file I confirm that index node is valid 87 | indexNode.IsDeleted = false; 88 | 89 | // Mask header as dirty for save on dispose 90 | Header.IsDirty = true; 91 | } 92 | 93 | public IndexNode Search(Guid id) 94 | { 95 | // Take the root node from inital index page 96 | IndexNode rootIndexNode = IndexFactory.GetRootIndexNode(this); 97 | 98 | var indexNode = IndexFactory.BinarySearch(id, rootIndexNode, this); 99 | 100 | // Returns null with not found the record, return false 101 | if (indexNode == null || indexNode.IsDeleted) 102 | return null; 103 | 104 | return indexNode; 105 | } 106 | 107 | public EntryInfo Read(Guid id, Stream stream) 108 | { 109 | // Search from index node 110 | var indexNode = Search(id); 111 | 112 | // If index node is null, not found the guid 113 | if (indexNode == null) 114 | return null; 115 | 116 | // Create a entry based on index node 117 | EntryInfo entry = new EntryInfo(indexNode); 118 | 119 | // Read data from the index pointer to stream 120 | DataFactory.ReadFile(indexNode, stream, this); 121 | 122 | return entry; 123 | } 124 | 125 | public FileDBStream OpenRead(Guid id) 126 | { 127 | // Open a FileDBStream and return to user 128 | var file = new FileDBStream(this, id); 129 | 130 | // If FileInfo is null, ID was not found 131 | return file.FileInfo == null ? null : file; 132 | } 133 | 134 | public bool Delete(Guid id) 135 | { 136 | // Search index node from guid 137 | var indexNode = Search(id); 138 | 139 | // If null, not found (return false) 140 | if (indexNode == null) 141 | return false; 142 | 143 | // Delete the index node logicaly 144 | indexNode.IsDeleted = true; 145 | 146 | // Add page (from index node) to cache and set as dirty 147 | CacheIndexPage.AddPage(indexNode.IndexPage, true); 148 | 149 | // Mark all data blocks (from data pages) as IsEmpty = true 150 | DataFactory.MarkAsEmpty(indexNode.DataPageID, this); 151 | 152 | // Set header as Dirty to be saved on dispose 153 | Header.IsDirty = true; 154 | 155 | return true; // Confirma a exclusão 156 | } 157 | 158 | public EntryInfo[] ListAllFiles() 159 | { 160 | // Get root index page from cache 161 | var pageIndex = CacheIndexPage.GetPage(Header.IndexRootPageID); 162 | bool cont = true; 163 | 164 | List list = new List(); 165 | 166 | while (cont) 167 | { 168 | for (int i = 0; i <= pageIndex.NodeIndex; i++) 169 | { 170 | // Convert node (if is not logicaly deleted) to Entry 171 | var node = pageIndex.Nodes[i]; 172 | if (!node.IsDeleted) 173 | list.Add(new EntryInfo(node)); 174 | } 175 | 176 | // Go to the next page 177 | if (pageIndex.NextPageID != uint.MaxValue) 178 | pageIndex = CacheIndexPage.GetPage(pageIndex.NextPageID); 179 | else 180 | cont = false; 181 | } 182 | 183 | return list.ToArray(); 184 | } 185 | 186 | public void PersistPages() 187 | { 188 | // Check if header is dirty and save to disk 189 | if (Header.IsDirty) 190 | { 191 | HeaderFactory.WriteToFile(Header, Writer); 192 | Header.IsDirty = false; 193 | } 194 | 195 | // Persist all index pages that are dirty 196 | CacheIndexPage.PersistPages(); 197 | } 198 | 199 | public void Dispose() 200 | { 201 | if (Writer != null) 202 | { 203 | // Unlock the file, prevent concurrence writing 204 | Writer.Unlock(Header.LOCKER_POS, 1); 205 | Writer.Close(); 206 | } 207 | 208 | Reader.Close(); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /FileDB/Exceptions/FileDBException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | public class FileDBException : ApplicationException 10 | { 11 | public FileDBException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | public FileDBException(string message, params object[] args) 17 | : base(string.Format(message, args)) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /FileDB/Factories/DataFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class DataFactory 10 | { 11 | public static uint GetStartDataPageID(Engine engine) 12 | { 13 | if (engine.Header.FreeDataPageID != uint.MaxValue) // I have free page inside the disk file. Use it 14 | { 15 | // Take the first free data page 16 | var startPage = PageFactory.GetDataPage(engine.Header.FreeDataPageID, engine.Reader, true); 17 | 18 | engine.Header.FreeDataPageID = startPage.NextPageID; // and point the free page to new free one 19 | 20 | // If the next page is MAX, fix too LastFreeData 21 | 22 | if(engine.Header.FreeDataPageID == uint.MaxValue) 23 | engine.Header.LastFreeDataPageID = uint.MaxValue; 24 | 25 | return startPage.PageID; 26 | } 27 | else // Don't have free data pages, create new one. 28 | { 29 | engine.Header.LastPageID++; 30 | return engine.Header.LastPageID; 31 | } 32 | } 33 | 34 | // Take a new data page on sequence and update the last 35 | public static DataPage GetNewDataPage(DataPage basePage, Engine engine) 36 | { 37 | if (basePage.NextPageID != uint.MaxValue) 38 | { 39 | PageFactory.WriteToFile(basePage, engine.Writer); // Write last page on disk 40 | 41 | var dataPage = PageFactory.GetDataPage(basePage.NextPageID, engine.Reader, false); 42 | 43 | engine.Header.FreeDataPageID = dataPage.NextPageID; 44 | 45 | if (engine.Header.FreeDataPageID == uint.MaxValue) 46 | engine.Header.LastFreeDataPageID = uint.MaxValue; 47 | 48 | return dataPage; 49 | } 50 | else 51 | { 52 | var pageID = ++engine.Header.LastPageID; 53 | DataPage newPage = new DataPage(pageID); 54 | basePage.NextPageID = newPage.PageID; 55 | PageFactory.WriteToFile(basePage, engine.Writer); // Write last page on disk 56 | return newPage; 57 | } 58 | } 59 | 60 | public static void InsertFile(IndexNode node, Stream stream, Engine engine) 61 | { 62 | DataPage dataPage = null; 63 | var buffer = new byte[DataPage.DATA_PER_PAGE]; 64 | uint totalBytes = 0; 65 | 66 | int read = 0; 67 | int dataPerPage = (int)DataPage.DATA_PER_PAGE; 68 | 69 | while ((read = stream.Read(buffer, 0, dataPerPage)) > 0) 70 | { 71 | totalBytes += (uint)read; 72 | 73 | if (dataPage == null) // First read 74 | dataPage = engine.GetPageData(node.DataPageID); 75 | else 76 | dataPage = GetNewDataPage(dataPage, engine); 77 | 78 | if (!dataPage.IsEmpty) // This is never to happend!! 79 | throw new FileDBException("Page {0} is not empty", dataPage.PageID); 80 | 81 | Array.Copy(buffer, dataPage.DataBlock, read); 82 | dataPage.IsEmpty = false; 83 | dataPage.DataBlockLength = (short)read; 84 | } 85 | 86 | // If the last page point to another one, i need to fix that 87 | if (dataPage.NextPageID != uint.MaxValue) 88 | { 89 | engine.Header.FreeDataPageID = dataPage.NextPageID; 90 | dataPage.NextPageID = uint.MaxValue; 91 | } 92 | 93 | // Salve the last page on disk 94 | PageFactory.WriteToFile(dataPage, engine.Writer); 95 | 96 | // Save on node index that file length 97 | node.FileLength = totalBytes; 98 | 99 | } 100 | 101 | public static void ReadFile(IndexNode node, Stream stream, Engine engine) 102 | { 103 | var dataPage = PageFactory.GetDataPage(node.DataPageID, engine.Reader, false); 104 | 105 | while (dataPage != null) 106 | { 107 | stream.Write(dataPage.DataBlock, 0, dataPage.DataBlockLength); 108 | 109 | if (dataPage.NextPageID == uint.MaxValue) 110 | dataPage = null; 111 | else 112 | dataPage = PageFactory.GetDataPage(dataPage.NextPageID, engine.Reader, false); 113 | } 114 | 115 | } 116 | 117 | public static void MarkAsEmpty(uint firstPageID, Engine engine) 118 | { 119 | var dataPage = PageFactory.GetDataPage(firstPageID, engine.Reader, true); 120 | uint lastPageID = uint.MaxValue; 121 | var cont = true; 122 | 123 | while (cont) 124 | { 125 | dataPage.IsEmpty = true; 126 | 127 | PageFactory.WriteToFile(dataPage, engine.Writer); 128 | 129 | if (dataPage.NextPageID != uint.MaxValue) 130 | { 131 | lastPageID = dataPage.NextPageID; 132 | dataPage = PageFactory.GetDataPage(lastPageID, engine.Reader, true); 133 | } 134 | else 135 | { 136 | cont = false; 137 | } 138 | } 139 | 140 | // Fix header to correct pointer 141 | if (engine.Header.FreeDataPageID == uint.MaxValue) // No free pages 142 | { 143 | engine.Header.FreeDataPageID = firstPageID; 144 | engine.Header.LastFreeDataPageID = lastPageID == uint.MaxValue ? firstPageID : lastPageID; 145 | } 146 | else 147 | { 148 | // Take the last statment available 149 | var lastPage = PageFactory.GetDataPage(engine.Header.LastFreeDataPageID, engine.Reader, true); 150 | 151 | // Point this last statent to first of next one 152 | if (lastPage.NextPageID != uint.MaxValue || !lastPage.IsEmpty) // This is never to happend!! 153 | throw new FileDBException("The page is not empty"); 154 | 155 | // Update this last page to first new empty page 156 | lastPage.NextPageID = firstPageID; 157 | 158 | // Save on disk this update 159 | PageFactory.WriteToFile(lastPage, engine.Writer); 160 | 161 | // Point header to the new empty page 162 | engine.Header.LastFreeDataPageID = lastPageID == uint.MaxValue ? firstPageID : lastPageID; 163 | } 164 | } 165 | 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /FileDB/Factories/FileFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class FileFactory 10 | { 11 | public static void CreateEmptyFile(BinaryWriter writer) 12 | { 13 | // Create new header instance 14 | var header = new Header(); 15 | 16 | header.IndexRootPageID = 0; 17 | header.FreeIndexPageID = 0; 18 | header.FreeDataPageID = uint.MaxValue; 19 | header.LastFreeDataPageID = uint.MaxValue; 20 | header.LastPageID = 0; 21 | 22 | HeaderFactory.WriteToFile(header, writer); 23 | 24 | // Create a first fixed index page 25 | var pageIndex = new IndexPage(0); 26 | pageIndex.NodeIndex = 0; 27 | pageIndex.NextPageID = uint.MaxValue; 28 | 29 | // Create first fixed index node, with fixed middle guid 30 | var indexNode = pageIndex.Nodes[0]; 31 | indexNode.ID = new Guid(new byte[] { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 }); 32 | indexNode.IsDeleted = true; 33 | indexNode.Right = new IndexLink(); 34 | indexNode.Left = new IndexLink(); 35 | indexNode.DataPageID = uint.MaxValue; 36 | indexNode.FileName = string.Empty; 37 | indexNode.FileExtension = string.Empty; 38 | 39 | PageFactory.WriteToFile(pageIndex, writer); 40 | 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /FileDB/Factories/HeaderFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class HeaderFactory 10 | { 11 | public static void ReadFromFile(Header header, BinaryReader reader) 12 | { 13 | // Seek the stream on 0 position to read header 14 | reader.BaseStream.Seek(0, SeekOrigin.Begin); 15 | 16 | // Make same validation on header file 17 | if (reader.ReadString(Header.FileID.Length) != Header.FileID) 18 | throw new FileDBException("The file is not a valid storage archive"); 19 | 20 | if (reader.ReadInt16() != Header.FileVersion) 21 | throw new FileDBException("The archive version is not valid"); 22 | 23 | header.IndexRootPageID = reader.ReadUInt32(); 24 | header.FreeIndexPageID = reader.ReadUInt32(); 25 | header.FreeDataPageID = reader.ReadUInt32(); 26 | header.LastFreeDataPageID = reader.ReadUInt32(); 27 | header.LastPageID = reader.ReadUInt32(); 28 | header.IsDirty = false; 29 | } 30 | 31 | public static void WriteToFile(Header header, BinaryWriter writer) 32 | { 33 | // Seek the stream on 0 position to save header 34 | writer.BaseStream.Seek(0, SeekOrigin.Begin); 35 | 36 | writer.Write(Header.FileID.ToBytes(Header.FileID.Length)); 37 | writer.Write(Header.FileVersion); 38 | 39 | writer.Write(header.IndexRootPageID); 40 | writer.Write(header.FreeIndexPageID); 41 | writer.Write(header.FreeDataPageID); 42 | writer.Write(header.LastFreeDataPageID); 43 | writer.Write(header.LastPageID); 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /FileDB/Factories/IndexFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class IndexFactory 10 | { 11 | public static IndexNode GetRootIndexNode(Engine engine) 12 | { 13 | IndexPage rootIndexPage = engine.CacheIndexPage.GetPage(engine.Header.IndexRootPageID); 14 | return rootIndexPage.Nodes[0]; 15 | } 16 | 17 | public static IndexNode BinaryInsert(EntryInfo target, IndexNode baseNode, Engine engine) 18 | { 19 | var dif = baseNode.ID.CompareTo(target.ID); 20 | 21 | if (dif == 1) // > Maior (Right) 22 | { 23 | if (baseNode.Right.IsEmpty) 24 | return BinaryInsertNode(baseNode.Right, baseNode, target, engine); 25 | else 26 | return BinaryInsert(target, GetChildIndexNode(baseNode.Right, engine), engine); 27 | } 28 | else if (dif == -1) // < Menor (Left) 29 | { 30 | if (baseNode.Left.IsEmpty) 31 | return BinaryInsertNode(baseNode.Left, baseNode, target, engine); 32 | else 33 | return BinaryInsert(target, GetChildIndexNode(baseNode.Left, engine), engine); 34 | } 35 | else 36 | { 37 | throw new FileDBException("Same GUID?!?"); 38 | } 39 | } 40 | 41 | private static IndexNode GetChildIndexNode(IndexLink link, Engine engine) 42 | { 43 | var pageIndex = engine.CacheIndexPage.GetPage(link.PageID); 44 | return pageIndex.Nodes[link.Index]; 45 | } 46 | 47 | private static IndexNode BinaryInsertNode(IndexLink baseLink, IndexNode baseNode, EntryInfo entry, Engine engine) 48 | { 49 | // Must insert my new nodo 50 | var pageIndex = engine.GetFreeIndexPage(); 51 | var newNode = pageIndex.Nodes[pageIndex.NodeIndex]; 52 | 53 | baseLink.PageID = pageIndex.PageID; 54 | baseLink.Index = pageIndex.NodeIndex; 55 | 56 | newNode.UpdateFromEntry(entry); 57 | newNode.DataPageID = DataFactory.GetStartDataPageID(engine); 58 | 59 | if (pageIndex.PageID != baseNode.IndexPage.PageID) 60 | engine.CacheIndexPage.AddPage(baseNode.IndexPage, true); 61 | 62 | engine.CacheIndexPage.AddPage(pageIndex, true); 63 | 64 | return newNode; 65 | } 66 | 67 | public static IndexNode BinarySearch(Guid target, IndexNode baseNode, Engine engine) 68 | { 69 | var dif = baseNode.ID.CompareTo(target); 70 | 71 | if (dif == 1) // > Maior (Right) 72 | { 73 | if (baseNode.Right.IsEmpty) // If there no ones on right, GUID not found 74 | return null; 75 | else 76 | return BinarySearch(target, GetChildIndexNode(baseNode.Right, engine), engine); // Recursive call on right node 77 | } 78 | else if (dif == -1) // < Menor (Left) 79 | { 80 | if (baseNode.Left.IsEmpty) // If there no ones on left, GUID not found 81 | return null; 82 | else 83 | return BinarySearch(target, GetChildIndexNode(baseNode.Left, engine), engine); // Recursive call on left node 84 | } 85 | else 86 | { 87 | // Found it 88 | return baseNode; 89 | } 90 | } 91 | 92 | 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /FileDB/Factories/PageFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class PageFactory 10 | { 11 | #region Read/Write Index Page 12 | 13 | public static void ReadFromFile(IndexPage indexPage, BinaryReader reader) 14 | { 15 | // Seek the stream to the fist byte on page 16 | long initPos = reader.Seek(Header.HEADER_SIZE + ((long)indexPage.PageID * BasePage.PAGE_SIZE)); 17 | 18 | if (reader.ReadByte() != (byte)PageType.Index) 19 | throw new FileDBException("PageID {0} is not a Index Page", indexPage.PageID); 20 | 21 | indexPage.NextPageID = reader.ReadUInt32(); 22 | indexPage.NodeIndex = reader.ReadByte(); 23 | 24 | // Seek the stream to end of header data page 25 | reader.Seek(initPos + IndexPage.HEADER_SIZE); 26 | 27 | for (int i = 0; i <= indexPage.NodeIndex; i++) 28 | { 29 | var node = indexPage.Nodes[i]; 30 | 31 | node.ID = reader.ReadGuid(); 32 | 33 | node.IsDeleted = reader.ReadBoolean(); 34 | 35 | node.Right.Index = reader.ReadByte(); 36 | node.Right.PageID = reader.ReadUInt32(); 37 | node.Left.Index = reader.ReadByte(); 38 | node.Left.PageID = reader.ReadUInt32(); 39 | 40 | node.DataPageID = reader.ReadUInt32(); 41 | 42 | node.FileName = reader.ReadString(IndexNode.FILENAME_SIZE); 43 | node.FileExtension = reader.ReadString(IndexNode.FILE_EXTENSION_SIZE); 44 | node.FileLength = reader.ReadUInt32(); 45 | } 46 | } 47 | 48 | public static void WriteToFile(IndexPage indexPage, BinaryWriter writer) 49 | { 50 | // Seek the stream to the fist byte on page 51 | long initPos = writer.Seek(Header.HEADER_SIZE + ((long)indexPage.PageID * BasePage.PAGE_SIZE)); 52 | 53 | // Write page header 54 | writer.Write((byte)indexPage.Type); 55 | writer.Write(indexPage.NextPageID); 56 | writer.Write(indexPage.NodeIndex); 57 | 58 | // Seek the stream to end of header index page 59 | writer.Seek(initPos + IndexPage.HEADER_SIZE); 60 | 61 | for (int i = 0; i <= indexPage.NodeIndex; i++) 62 | { 63 | var node = indexPage.Nodes[i]; 64 | 65 | writer.Write(node.ID); 66 | 67 | writer.Write(node.IsDeleted); 68 | 69 | writer.Write(node.Right.Index); 70 | writer.Write(node.Right.PageID); 71 | writer.Write(node.Left.Index); 72 | writer.Write(node.Left.PageID); 73 | 74 | writer.Write(node.DataPageID); 75 | 76 | writer.Write(node.FileName.ToBytes(IndexNode.FILENAME_SIZE)); 77 | writer.Write(node.FileExtension.ToBytes(IndexNode.FILE_EXTENSION_SIZE)); 78 | writer.Write(node.FileLength); 79 | } 80 | 81 | } 82 | 83 | #endregion 84 | 85 | #region Read/Write Data Page 86 | 87 | public static void ReadFromFile(DataPage dataPage, BinaryReader reader, bool onlyHeader) 88 | { 89 | // Seek the stream on first byte from data page 90 | long initPos = reader.Seek(Header.HEADER_SIZE + ((long)dataPage.PageID * BasePage.PAGE_SIZE)); 91 | 92 | if (reader.ReadByte() != (byte)PageType.Data) 93 | throw new FileDBException("PageID {0} is not a Data Page", dataPage.PageID); 94 | 95 | dataPage.NextPageID = reader.ReadUInt32(); 96 | dataPage.IsEmpty = reader.ReadBoolean(); 97 | dataPage.DataBlockLength = reader.ReadInt16(); 98 | 99 | // If page is empty or onlyHeader parameter, I don't read data content 100 | if (!dataPage.IsEmpty && !onlyHeader) 101 | { 102 | // Seek the stream at the end of page header 103 | reader.Seek(initPos + DataPage.HEADER_SIZE); 104 | 105 | // Read all bytes from page 106 | dataPage.DataBlock = reader.ReadBytes(dataPage.DataBlockLength); 107 | } 108 | } 109 | 110 | public static void WriteToFile(DataPage dataPage, BinaryWriter writer) 111 | { 112 | // Seek the stream on first byte from data page 113 | long initPos = writer.Seek(Header.HEADER_SIZE + ((long)dataPage.PageID * BasePage.PAGE_SIZE)); 114 | 115 | // Write data page header 116 | writer.Write((byte)dataPage.Type); 117 | writer.Write(dataPage.NextPageID); 118 | writer.Write(dataPage.IsEmpty); 119 | writer.Write(dataPage.DataBlockLength); 120 | 121 | // I will only save data content if the page is not empty 122 | if (!dataPage.IsEmpty) 123 | { 124 | // Seek the stream at the end of page header 125 | writer.Seek(initPos + DataPage.HEADER_SIZE); 126 | 127 | writer.Write(dataPage.DataBlock, 0, (int)dataPage.DataBlockLength); 128 | } 129 | } 130 | 131 | #endregion 132 | 133 | #region Get Pages from File 134 | 135 | public static IndexPage GetIndexPage(uint pageID, BinaryReader reader) 136 | { 137 | var indexPage = new IndexPage(pageID); 138 | ReadFromFile(indexPage, reader); 139 | return indexPage; 140 | } 141 | 142 | public static DataPage GetDataPage(uint pageID, BinaryReader reader, bool onlyHeader) 143 | { 144 | var dataPage = new DataPage(pageID); 145 | ReadFromFile(dataPage, reader, onlyHeader); 146 | return dataPage; 147 | } 148 | 149 | public static BasePage GetBasePage(uint pageID, BinaryReader reader) 150 | { 151 | // Seek the stream at begin of page 152 | long initPos = reader.Seek(Header.HEADER_SIZE + ((long)pageID * BasePage.PAGE_SIZE)); 153 | 154 | if (reader.ReadByte() == (byte)PageType.Index) 155 | return GetIndexPage(pageID, reader); 156 | else 157 | return GetDataPage(pageID, reader, true); 158 | } 159 | 160 | #endregion 161 | 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /FileDB/FileDB.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | /// 10 | /// FileDB main class. 11 | /// 12 | public partial class FileDB : IDisposable 13 | { 14 | private FileStream _fileStream = null; 15 | private Engine _engine = null; 16 | private DebugFile _debug = null; 17 | 18 | /// 19 | /// Open a database file 20 | /// 21 | /// Database filename (eg: C:\Data\MyDB.dat) 22 | /// Acces mode (Read|ReadWrite|Write) 23 | public FileDB(string fileName, FileAccess fileAccess) 24 | { 25 | Connect(fileName, fileAccess); 26 | } 27 | 28 | private void Connect(string fileName, FileAccess fileAccess) 29 | { 30 | if (!File.Exists(fileName)) 31 | FileDB.CreateEmptyFile(fileName); 32 | 33 | // Não permite acesso somente gravação (transforma em leitura/gravação) 34 | var fa = fileAccess == FileAccess.Write || fileAccess == FileAccess.ReadWrite ? FileAccess.ReadWrite : FileAccess.Read; 35 | 36 | _fileStream = new FileStream(fileName, FileMode.Open, fa, FileShare.ReadWrite, (int)BasePage.PAGE_SIZE, FileOptions.None); 37 | 38 | _engine = new Engine(_fileStream); 39 | } 40 | 41 | /// 42 | /// Store a disk file inside database 43 | /// 44 | /// Full path to file (eg: C:\Temp\MyPhoto.jpg) 45 | /// EntryInfo with information store 46 | public EntryInfo Store(string fileName) 47 | { 48 | using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) 49 | { 50 | return Store(fileName, stream); 51 | } 52 | } 53 | 54 | /// 55 | /// Store a stream inside database 56 | /// 57 | /// Just a name of file, to get future reference (eg: MyPhoto.jpg) 58 | /// Stream thats contains the file 59 | /// EntryInfo with information store 60 | public EntryInfo Store(string fileName, Stream input) 61 | { 62 | var entry = new EntryInfo(fileName); 63 | _engine.Write(entry, input); 64 | return entry; 65 | } 66 | 67 | internal void Store(EntryInfo entry, Stream input) 68 | { 69 | _engine.Write(entry, input); 70 | } 71 | 72 | /// 73 | /// Retrieve a file inside a database 74 | /// 75 | /// A Guid that references to file 76 | /// Path to save the file 77 | /// EntryInfo with information about the file 78 | public EntryInfo Read(Guid id, string fileName) 79 | { 80 | using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write)) 81 | { 82 | return Read(id, stream); 83 | } 84 | } 85 | 86 | /// 87 | /// Retrieve a file inside a database 88 | /// 89 | /// A Guid that references to file 90 | /// Output strem to save the file 91 | /// EntryInfo with information about the file 92 | public EntryInfo Read(Guid id, Stream output) 93 | { 94 | return _engine.Read(id, output); 95 | } 96 | 97 | /// 98 | /// Retrieve a file inside a database returning a FileDBStream to read 99 | /// 100 | /// A Guid that references to file 101 | /// A FileDBStream ready to be readed or null if ID was not found 102 | public FileDBStream OpenRead(Guid id) 103 | { 104 | return _engine.OpenRead(id); 105 | } 106 | 107 | /// 108 | /// Search for a file inside database BUT get only EntryInfo information (don't copy the file) 109 | /// 110 | /// File ID 111 | /// EntryInfo with file information or null with not found 112 | public EntryInfo Search(Guid id) 113 | { 114 | var indexNode = _engine.Search(id); 115 | 116 | if (indexNode == null) 117 | return null; 118 | else 119 | return new EntryInfo(indexNode); 120 | } 121 | 122 | /// 123 | /// Delete a file inside database 124 | /// 125 | /// Guid ID from a file 126 | /// True when the file was deleted or False when not found 127 | public bool Delete(Guid id) 128 | { 129 | return _engine.Delete(id); 130 | } 131 | 132 | /// 133 | /// List all files inside a FileDB 134 | /// 135 | /// Array with all files 136 | public EntryInfo[] ListFiles() 137 | { 138 | return _engine.ListAllFiles(); 139 | } 140 | 141 | /// 142 | /// Export all files inside FileDB database to a directory 143 | /// 144 | /// Directory name 145 | public void Export(string directory) 146 | { 147 | this.Export(directory, "{filename}.{id}.{extension}"); 148 | } 149 | 150 | /// 151 | /// Export all files inside FileDB database to a directory 152 | /// 153 | /// Directory name 154 | /// File Pattern. Use keys: {id} {extension} {filename}. Eg: "{filename}.{id}.{extension}" 155 | public void Export(string directory, string filePattern) 156 | { 157 | if (!Directory.Exists(directory)) 158 | Directory.CreateDirectory(directory); 159 | 160 | var files = ListFiles(); 161 | 162 | foreach (var file in files) 163 | { 164 | var fileName = filePattern.Replace("{id}", file.ID.ToString()) 165 | .Replace("{filename}", Path.GetFileNameWithoutExtension(file.FileName)) 166 | .Replace("{extension}", Path.GetExtension(file.FileName).Replace(".", "")); 167 | 168 | Read(file.ID, Path.Combine(directory, fileName)); 169 | } 170 | } 171 | 172 | /// 173 | /// Shrink datafile 174 | /// 175 | public void Shrink() 176 | { 177 | var dbFileName = _fileStream.Name; 178 | var fileAccess = _fileStream.CanWrite ? FileAccess.ReadWrite : FileAccess.Read; 179 | var tempFile = Path.GetDirectoryName(dbFileName) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(dbFileName) + ".temp" + Path.GetExtension(dbFileName); 180 | 181 | if (File.Exists(tempFile)) 182 | File.Delete(tempFile); 183 | 184 | var entries = ListFiles(); 185 | 186 | FileDB.CreateEmptyFile(tempFile, false); 187 | 188 | using (var tempDb = new FileDB(tempFile, FileAccess.ReadWrite)) 189 | { 190 | foreach (var entry in entries) 191 | { 192 | using (var stream = new MemoryStream()) 193 | { 194 | Read(entry.ID, stream); 195 | stream.Seek(0, SeekOrigin.Begin); 196 | tempDb.Store(entry, stream); 197 | } 198 | } 199 | } 200 | 201 | Dispose(); 202 | 203 | File.Delete(dbFileName); 204 | File.Move(tempFile, dbFileName); 205 | 206 | Connect(dbFileName, fileAccess); 207 | } 208 | 209 | public void Dispose() 210 | { 211 | if (_engine != null) 212 | { 213 | _engine.PersistPages(); // Persiste as paginas/header que ficaram em memória 214 | 215 | if (_fileStream.CanWrite) 216 | _fileStream.Flush(); 217 | 218 | _engine.Dispose(); 219 | 220 | _fileStream.Dispose(); 221 | } 222 | } 223 | 224 | /// 225 | /// Print debug information about FileDB Structure 226 | /// 227 | public DebugFile Debug 228 | { 229 | get 230 | { 231 | if (_debug == null) 232 | _debug = new DebugFile(_engine); 233 | 234 | return _debug; 235 | } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /FileDB/FileDB.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {5F72F6FB-89BC-439F-989A-5B9581A50CC5} 9 | Library 10 | Properties 11 | Numeria 12 | FileDB 13 | v3.5 14 | 512 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 74 | -------------------------------------------------------------------------------- /FileDB/Helper/BinaryReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal static class BinaryReaderExtensions 10 | { 11 | public static string ReadString(this BinaryReader reader, int size) 12 | { 13 | var bytes = reader.ReadBytes(size); 14 | string str = Encoding.UTF8.GetString(bytes); 15 | return str.Replace((char)0, ' ').Trim(); 16 | } 17 | 18 | public static Guid ReadGuid(this BinaryReader reader) 19 | { 20 | var bytes = reader.ReadBytes(16); 21 | return new Guid(bytes); 22 | } 23 | 24 | public static DateTime ReadDateTime(this BinaryReader reader) 25 | { 26 | var ticks = reader.ReadInt64(); 27 | return new DateTime(ticks); 28 | } 29 | 30 | public static long Seek(this BinaryReader reader, long position) 31 | { 32 | return reader.BaseStream.Seek(position, SeekOrigin.Begin); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /FileDB/Helper/BinaryWriterExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using System.Threading; 7 | 8 | namespace Numeria.IO 9 | { 10 | internal static class BinaryWriterExtensions 11 | { 12 | private const int MAX_TRY_LOCK_FILE = 50; // Max try to lock the data file 13 | private const int DELAY_TRY_LOCK_FILE = 50; // in miliseconds 14 | 15 | public static void Write(this BinaryWriter writer, Guid guid) 16 | { 17 | writer.Write(guid.ToByteArray()); 18 | } 19 | 20 | public static void Write(this BinaryWriter writer, DateTime dateTime) 21 | { 22 | writer.Write(dateTime.Ticks); 23 | } 24 | 25 | public static long Seek(this BinaryWriter writer, long position) 26 | { 27 | return writer.BaseStream.Seek(position, SeekOrigin.Begin); 28 | } 29 | 30 | public static void Lock(this BinaryWriter writer, long position, long length) 31 | { 32 | var fileStream = writer.BaseStream as FileStream; 33 | 34 | TryLockFile(fileStream, position, length, 0); 35 | } 36 | 37 | private static void TryLockFile(FileStream fileStream, long position, long length, int tryCount) 38 | { 39 | try 40 | { 41 | fileStream.Lock(position, length); 42 | } 43 | catch (IOException ex) 44 | { 45 | if (ex.IsLockException()) 46 | { 47 | if (tryCount >= DELAY_TRY_LOCK_FILE) 48 | throw new FileDBException("Database file is in lock for a long time"); 49 | 50 | Thread.Sleep(tryCount * DELAY_TRY_LOCK_FILE); 51 | 52 | TryLockFile(fileStream, position, length, ++tryCount); 53 | } 54 | else 55 | throw ex; 56 | } 57 | } 58 | 59 | public static void Unlock(this BinaryWriter writer, long position, long length) 60 | { 61 | var fileStream = writer.BaseStream as FileStream; 62 | fileStream.Unlock(position, length); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /FileDB/Helper/CacheIndexPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal delegate void ReleasePageIndexFromCache(IndexPage page); 10 | 11 | internal class CacheIndexPage 12 | { 13 | public const int CACHE_SIZE = 200; 14 | 15 | private BinaryReader _reader; 16 | private BinaryWriter _writer; 17 | private Dictionary _cache; 18 | private uint _rootPageID; 19 | 20 | public CacheIndexPage(BinaryReader reader, BinaryWriter writer, uint rootPageID) 21 | { 22 | _reader = reader; 23 | _writer = writer; 24 | _cache = new Dictionary(); 25 | _rootPageID = rootPageID; 26 | } 27 | 28 | public IndexPage GetPage(uint pageID) 29 | { 30 | if (_cache.ContainsKey(pageID)) 31 | return _cache[pageID]; 32 | 33 | var indexPage = PageFactory.GetIndexPage(pageID, _reader); 34 | 35 | AddPage(indexPage, false); 36 | 37 | return indexPage; 38 | } 39 | 40 | public void AddPage(IndexPage indexPage) 41 | { 42 | AddPage(indexPage, false); 43 | } 44 | 45 | public void AddPage(IndexPage indexPage, bool markAsDirty) 46 | { 47 | if(!_cache.ContainsKey(indexPage.PageID)) 48 | { 49 | if(_cache.Count >= CACHE_SIZE) 50 | { 51 | // Remove fist page that are not the root page (because I use too much) 52 | var pageToRemove = _cache.First(x => x.Key != _rootPageID); 53 | 54 | if (pageToRemove.Value.IsDirty) 55 | { 56 | PageFactory.WriteToFile(pageToRemove.Value, _writer); 57 | pageToRemove.Value.IsDirty = false; 58 | } 59 | 60 | _cache.Remove(pageToRemove.Key); 61 | } 62 | 63 | _cache.Add(indexPage.PageID, indexPage); 64 | } 65 | 66 | if(markAsDirty) 67 | indexPage.IsDirty = true; 68 | } 69 | 70 | public void PersistPages() 71 | { 72 | // Check which pages is dirty and need to saved on disk 73 | var pagesToPersist = _cache.Values.Where(x => x.IsDirty).ToArray(); 74 | 75 | if (pagesToPersist.Length > 0) 76 | { 77 | foreach (var indexPage in pagesToPersist) 78 | { 79 | PageFactory.WriteToFile(indexPage, _writer); 80 | indexPage.IsDirty = false; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /FileDB/Helper/DebugFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | public class DebugFile 10 | { 11 | private Engine _engine; 12 | 13 | internal DebugFile(Engine engine) 14 | { 15 | _engine = engine; 16 | } 17 | 18 | public string DisplayPages() 19 | { 20 | var sb = new StringBuilder(); 21 | 22 | sb.AppendLine("Constants:"); 23 | sb.AppendLine("============="); 24 | sb.AppendLine("BasePage.PAGE_SIZE : " + BasePage.PAGE_SIZE); 25 | sb.AppendLine("IndexPage.HEADER_SIZE : " + IndexPage.HEADER_SIZE); 26 | sb.AppendLine("IndexPage.NODES_PER_PAGE : " + IndexPage.NODES_PER_PAGE); 27 | sb.AppendLine("DataPage.HEADER_SIZE : " + DataPage.HEADER_SIZE); 28 | sb.AppendLine("DataPage.DATA_PER_PAGE : " + DataPage.DATA_PER_PAGE); 29 | 30 | sb.AppendLine(); 31 | sb.AppendLine("Header:"); 32 | sb.AppendLine("============="); 33 | sb.AppendLine("IndexRootPageID : " + _engine.Header.IndexRootPageID.Fmt()); 34 | sb.AppendLine("FreeIndexPageID : " + _engine.Header.FreeIndexPageID.Fmt()); 35 | sb.AppendLine("FreeDataPageID : " + _engine.Header.FreeDataPageID.Fmt()); 36 | sb.AppendLine("LastFreeDataPageID : " + _engine.Header.LastFreeDataPageID.Fmt()); 37 | sb.AppendLine("LastPageID : " + _engine.Header.LastPageID.Fmt()); 38 | 39 | sb.AppendLine(); 40 | sb.AppendLine("Pages:"); 41 | sb.AppendLine("============="); 42 | 43 | for (uint i = 0; i <= _engine.Header.LastPageID; i++) 44 | { 45 | BasePage page = PageFactory.GetBasePage(i, _engine.Reader); 46 | 47 | sb.AppendFormat("[{0}] >> [{1}] ({2}) ", 48 | page.PageID.Fmt(), page.NextPageID.Fmt(), page.Type == PageType.Data ? "D" : "I"); 49 | 50 | if (page.Type == PageType.Data) 51 | { 52 | var dataPage = (DataPage)page; 53 | 54 | if (dataPage.IsEmpty) 55 | sb.Append("Empty"); 56 | else 57 | sb.AppendFormat("Bytes: {0}", dataPage.DataBlockLength); 58 | } 59 | else 60 | { 61 | var indexPage = (IndexPage)page; 62 | 63 | sb.AppendFormat("Keys: {0}", indexPage.NodeIndex + 1); 64 | } 65 | 66 | 67 | sb.AppendLine(); 68 | } 69 | 70 | 71 | return sb.ToString(); 72 | } 73 | } 74 | 75 | internal static class Display 76 | { 77 | public static string Fmt(this uint val) 78 | { 79 | if (val == uint.MaxValue) 80 | return "----"; 81 | else 82 | return val.ToString("0000"); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /FileDB/Helper/FileDBExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | public partial class FileDB 10 | { 11 | /// 12 | /// Store a file inside the database 13 | /// 14 | /// Database path/filname (eg: C:\Temp\MyDB.dat) 15 | /// Filename/Path to read file (eg: C:\Temp\MyPhoto.jpg) 16 | /// EntryInfo with 17 | public static EntryInfo Store(string dbFileName, string fileName) 18 | { 19 | using (FileStream input = new FileStream(fileName, FileMode.Open, FileAccess.Read)) 20 | { 21 | return Store(dbFileName, fileName, input); 22 | } 23 | } 24 | 25 | /// 26 | /// Store a file inside the database 27 | /// 28 | /// Database path/filname (eg: C:\Temp\MyDB.dat) 29 | /// Filename to associate with file (eg: MyPhoto.jpg) 30 | /// Stream with a file content 31 | /// EntryInfo with file information 32 | public static EntryInfo Store(string dbFileName, string fileName, Stream input) 33 | { 34 | using (var db = new FileDB(dbFileName, FileAccess.ReadWrite)) 35 | { 36 | return db.Store(fileName, input); 37 | } 38 | } 39 | 40 | /// 41 | /// Read a file inside the database file 42 | /// 43 | /// Database path/filname (eg: C:\Temp\MyDB.dat) 44 | /// File ID 45 | /// Filename/Path to save the file (eg: C:\Temp\MyPhoto.jpg) 46 | /// EntryInfo with file information 47 | public static EntryInfo Read(string dbFileName, Guid id, string fileName) 48 | { 49 | using (var db = new FileDB(dbFileName, FileAccess.Read)) 50 | { 51 | return db.Read(id, fileName); 52 | } 53 | } 54 | 55 | /// 56 | /// Read a file inside the database file 57 | /// 58 | /// Database path/filname (eg: C:\Temp\MyDB.dat) 59 | /// File ID 60 | /// Stream to save the file 61 | /// EntryInfo with file information 62 | public static EntryInfo Read(string dbFileName, Guid id, Stream output) 63 | { 64 | using (var db = new FileDB(dbFileName, FileAccess.Read)) 65 | { 66 | return db.Read(id, output); 67 | } 68 | } 69 | 70 | /// 71 | /// Delete a file inside a database 72 | /// 73 | /// Database path/filname (eg: C:\Temp\MyDB.dat) 74 | /// Array with all files identities 75 | public static EntryInfo[] ListFiles(string dbFileName) 76 | { 77 | using (var db = new FileDB(dbFileName, FileAccess.Read)) 78 | { 79 | return db.ListFiles(); 80 | } 81 | } 82 | 83 | /// 84 | /// Delete a file inside a database 85 | /// 86 | /// Database path/filname (eg: C:\Temp\MyDB.dat) 87 | /// Guid of file 88 | /// True with found and delete the file, otherwise false 89 | public static bool Delete(string dbFileName, Guid id) 90 | { 91 | using (var db = new FileDB(dbFileName, FileAccess.ReadWrite)) 92 | { 93 | return db.Delete(id); 94 | } 95 | } 96 | 97 | /// 98 | /// Create a new database file 99 | /// 100 | /// Database path/filname (eg: C:\Temp\MyDB.dat) 101 | public static void CreateEmptyFile(string dbFileName) 102 | { 103 | CreateEmptyFile(dbFileName, true); 104 | } 105 | 106 | /// 107 | /// Create a new database file 108 | /// 109 | /// Database path/filname (eg: C:\Temp\MyDB.dat) 110 | /// True to ignore the file if already exists, otherise, throw a exception 111 | public static void CreateEmptyFile(string dbFileName, bool ignoreIfExists) 112 | { 113 | if (File.Exists(dbFileName)) 114 | { 115 | if (ignoreIfExists) 116 | return; 117 | else 118 | throw new FileDBException("Database file {0} already exists", dbFileName); 119 | } 120 | 121 | using (FileStream fileStream = new FileStream(dbFileName, FileMode.CreateNew, FileAccess.Write)) 122 | { 123 | using(BinaryWriter writer = new BinaryWriter(fileStream)) 124 | { 125 | FileFactory.CreateEmptyFile(writer); 126 | } 127 | } 128 | } 129 | 130 | /// 131 | /// Shrink database file 132 | /// 133 | /// Path to database file (eg: C:\Temp\MyDB.dat) 134 | public static void Shrink(string dbFileName) 135 | { 136 | using (var db = new FileDB(dbFileName, FileAccess.Read)) 137 | { 138 | db.Shrink(); 139 | } 140 | } 141 | 142 | /// 143 | /// Export all file inside a database to a directory 144 | /// 145 | /// FileDB database file 146 | /// Directory to export files 147 | /// File Pattern. Use keys: {id} {extension} {filename}. Eg: "{filename}.{id}.{extension}" 148 | public static void Export(string dbFileName, string directory, string filePattern) 149 | { 150 | using (var db = new FileDB(dbFileName, FileAccess.Read)) 151 | { 152 | db.Export(directory, filePattern); 153 | } 154 | } 155 | 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /FileDB/Helper/IOExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace Numeria.IO 9 | { 10 | internal static class IOExceptionExtensions 11 | { 12 | public static bool IsLockException(this IOException exception) 13 | { 14 | int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1); 15 | return errorCode == 32 || errorCode == 33; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /FileDB/Helper/MimeTypeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Numeria.IO 7 | { 8 | internal class MimeTypeConverter 9 | { 10 | public static string Convert(string extension) 11 | { 12 | var ext = extension.Replace(".", "").ToLower(); 13 | var r = string.Empty; 14 | 15 | switch (ext) 16 | { 17 | case "3dm": r = "x-world/x-3dmf"; break; 18 | case "3dmf": r = "x-world/x-3dmf"; break; 19 | case "a": r = "application/octet-stream"; break; 20 | case "aab": r = "application/x-authorware-bin"; break; 21 | case "aam": r = "application/x-authorware-map"; break; 22 | case "aas": r = "application/x-authorware-seg"; break; 23 | case "abc": r = "text/vnd.abc"; break; 24 | case "acgi": r = "text/html"; break; 25 | case "afl": r = "video/animaflex"; break; 26 | case "ai": r = "application/postscript"; break; 27 | case "aif": r = "audio/aiff"; break; 28 | case "aifc": r = "audio/aiff"; break; 29 | case "aiff": r = "audio/aiff"; break; 30 | case "aim": r = "application/x-aim"; break; 31 | case "aip": r = "text/x-audiosoft-intra"; break; 32 | case "ani": r = "application/x-navi-animation"; break; 33 | case "aos": r = "application/x-nokia-9000-communicator-add-on-software"; break; 34 | case "aps": r = "application/mime"; break; 35 | case "arc": r = "application/octet-stream"; break; 36 | case "arj": r = "application/arj"; break; 37 | case "art": r = "image/x-jg"; break; 38 | case "asf": r = "video/x-ms-asf"; break; 39 | case "asm": r = "text/x-asm"; break; 40 | case "asp": r = "text/asp"; break; 41 | case "asx": r = "video/x-ms-asf"; break; 42 | case "au": r = "audio/basic"; break; 43 | case "avi": r = "video/avi"; break; 44 | case "avs": r = "video/avs-video"; break; 45 | case "bcpio": r = "application/x-bcpio"; break; 46 | case "bin": r = "application/octet-stream"; break; 47 | case "bm": r = "image/bmp"; break; 48 | case "bmp": r = "image/bmp"; break; 49 | case "boo": r = "application/book"; break; 50 | case "book": r = "application/book"; break; 51 | case "boz": r = "application/x-bzip2"; break; 52 | case "bsh": r = "application/x-bsh"; break; 53 | case "bz": r = "application/x-bzip"; break; 54 | case "bz2": r = "application/x-bzip2"; break; 55 | case "c": r = "text/plain"; break; 56 | case "c++": r = "text/plain"; break; 57 | case "cat": r = "application/vnd.ms-pki.seccat"; break; 58 | case "cc": r = "text/plain"; break; 59 | case "ccad": r = "application/clariscad"; break; 60 | case "cco": r = "application/x-cocoa"; break; 61 | case "cdf": r = "application/cdf"; break; 62 | case "cer": r = "application/pkix-cert"; break; 63 | case "cha": r = "application/x-chat"; break; 64 | case "chat": r = "application/x-chat"; break; 65 | case "class": r = "application/java"; break; 66 | case "com": r = "application/octet-stream"; break; 67 | case "conf": r = "text/plain"; break; 68 | case "cpio": r = "application/x-cpio"; break; 69 | case "cpp": r = "text/x-c"; break; 70 | case "cpt": r = "application/x-cpt"; break; 71 | case "crl": r = "application/pkcs-crl"; break; 72 | case "crt": r = "application/pkix-cert"; break; 73 | case "csh": r = "application/x-csh"; break; 74 | case "css": r = "text/css"; break; 75 | case "cxx": r = "text/plain"; break; 76 | case "dcr": r = "application/x-director"; break; 77 | case "deepv": r = "application/x-deepv"; break; 78 | case "def": r = "text/plain"; break; 79 | case "der": r = "application/x-x509-ca-cert"; break; 80 | case "dif": r = "video/x-dv"; break; 81 | case "dir": r = "application/x-director"; break; 82 | case "dl": r = "video/dl"; break; 83 | case "doc": r = "application/msword"; break; 84 | case "dot": r = "application/msword"; break; 85 | case "dp": r = "application/commonground"; break; 86 | case "drw": r = "application/drafting"; break; 87 | case "dump": r = "application/octet-stream"; break; 88 | case "dv": r = "video/x-dv"; break; 89 | case "dvi": r = "application/x-dvi"; break; 90 | case "dwf": r = "model/vnd.dwf"; break; 91 | case "dwg": r = "image/vnd.dwg"; break; 92 | case "dxf": r = "image/vnd.dwg"; break; 93 | case "dxr": r = "application/x-director"; break; 94 | case "el": r = "text/x-script.elisp"; break; 95 | case "elc": r = "application/x-elc"; break; 96 | case "env": r = "application/x-envoy"; break; 97 | case "eps": r = "application/postscript"; break; 98 | case "es": r = "application/x-esrehber"; break; 99 | case "etx": r = "text/x-setext"; break; 100 | case "evy": r = "application/envoy"; break; 101 | case "exe": r = "application/octet-stream"; break; 102 | case "f": r = "text/plain"; break; 103 | case "f77": r = "text/x-fortran"; break; 104 | case "f90": r = "text/plain"; break; 105 | case "fdf": r = "application/vnd.fdf"; break; 106 | case "fif": r = "image/fif"; break; 107 | case "fli": r = "video/fli"; break; 108 | case "flo": r = "image/florian"; break; 109 | case "flx": r = "text/vnd.fmi.flexstor"; break; 110 | case "fmf": r = "video/x-atomic3d-feature"; break; 111 | case "for": r = "text/x-fortran"; break; 112 | case "fpx": r = "image/vnd.fpx"; break; 113 | case "frl": r = "application/freeloader"; break; 114 | case "funk": r = "audio/make"; break; 115 | case "g": r = "text/plain"; break; 116 | case "g3": r = "image/g3fax"; break; 117 | case "gif": r = "image/gif"; break; 118 | case "gl": r = "video/gl"; break; 119 | case "gsd": r = "audio/x-gsm"; break; 120 | case "gsm": r = "audio/x-gsm"; break; 121 | case "gsp": r = "application/x-gsp"; break; 122 | case "gss": r = "application/x-gss"; break; 123 | case "gtar": r = "application/x-gtar"; break; 124 | case "gz": r = "application/x-gzip"; break; 125 | case "gzip": r = "application/x-gzip"; break; 126 | case "h": r = "text/plain"; break; 127 | case "hdf": r = "application/x-hdf"; break; 128 | case "help": r = "application/x-helpfile"; break; 129 | case "hgl": r = "application/vnd.hp-hpgl"; break; 130 | case "hh": r = "text/plain"; break; 131 | case "hlb": r = "text/x-script"; break; 132 | case "hlp": r = "application/hlp"; break; 133 | case "hpg": r = "application/vnd.hp-hpgl"; break; 134 | case "hpgl": r = "application/vnd.hp-hpgl"; break; 135 | case "hqx": r = "application/binhex"; break; 136 | case "hta": r = "application/hta"; break; 137 | case "htc": r = "text/x-component"; break; 138 | case "htm": r = "text/html"; break; 139 | case "html": r = "text/html"; break; 140 | case "htmls": r = "text/html"; break; 141 | case "htt": r = "text/webviewhtml"; break; 142 | case "htx": r = "text/html"; break; 143 | case "ice": r = "x-conference/x-cooltalk"; break; 144 | case "ico": r = "image/x-icon"; break; 145 | case "idc": r = "text/plain"; break; 146 | case "ief": r = "image/ief"; break; 147 | case "iefs": r = "image/ief"; break; 148 | case "iges": r = "application/iges"; break; 149 | case "igs": r = "application/iges"; break; 150 | case "ima": r = "application/x-ima"; break; 151 | case "imap": r = "application/x-httpd-imap"; break; 152 | case "inf": r = "application/inf"; break; 153 | case "ins": r = "application/x-internett-signup"; break; 154 | case "ip": r = "application/x-ip2"; break; 155 | case "isu": r = "video/x-isvideo"; break; 156 | case "it": r = "audio/it"; break; 157 | case "iv": r = "application/x-inventor"; break; 158 | case "ivr": r = "i-world/i-vrml"; break; 159 | case "ivy": r = "application/x-livescreen"; break; 160 | case "jam": r = "audio/x-jam"; break; 161 | case "jav": r = "text/plain"; break; 162 | case "java": r = "text/plain"; break; 163 | case "jcm": r = "application/x-java-commerce"; break; 164 | case "jfif": r = "image/jpeg"; break; 165 | case "jfif-tbnl": r = "image/jpeg"; break; 166 | case "jpe": r = "image/jpeg"; break; 167 | case "jpeg": r = "image/jpeg"; break; 168 | case "jpg": r = "image/jpeg"; break; 169 | case "jps": r = "image/x-jps"; break; 170 | case "js": r = "application/x-javascript"; break; 171 | case "jut": r = "image/jutvision"; break; 172 | case "kar": r = "audio/midi"; break; 173 | case "ksh": r = "application/x-ksh"; break; 174 | case "la": r = "audio/nspaudio"; break; 175 | case "lam": r = "audio/x-liveaudio"; break; 176 | case "latex": r = "application/x-latex"; break; 177 | case "lha": r = "application/octet-stream"; break; 178 | case "lhx": r = "application/octet-stream"; break; 179 | case "list": r = "text/plain"; break; 180 | case "lma": r = "audio/nspaudio"; break; 181 | case "log": r = "text/plain"; break; 182 | case "lsp": r = "application/x-lisp"; break; 183 | case "lst": r = "text/plain"; break; 184 | case "lsx": r = "text/x-la-asf"; break; 185 | case "ltx": r = "application/x-latex"; break; 186 | case "lzh": r = "application/octet-stream"; break; 187 | case "lzx": r = "application/octet-stream"; break; 188 | case "m": r = "text/plain"; break; 189 | case "m1v": r = "video/mpeg"; break; 190 | case "m2a": r = "audio/mpeg"; break; 191 | case "m2v": r = "video/mpeg"; break; 192 | case "m3u": r = "audio/x-mpequrl"; break; 193 | case "man": r = "application/x-troff-man"; break; 194 | case "map": r = "application/x-navimap"; break; 195 | case "mar": r = "text/plain"; break; 196 | case "mbd": r = "application/mbedlet"; break; 197 | case "mc$": r = "application/x-magic-cap-package-1.0"; break; 198 | case "mcd": r = "application/mcad"; break; 199 | case "mcf": r = "text/mcf"; break; 200 | case "mcp": r = "application/netmc"; break; 201 | case "me": r = "application/x-troff-me"; break; 202 | case "mht": r = "message/rfc822"; break; 203 | case "mhtml": r = "message/rfc822"; break; 204 | case "mid": r = "audio/midi"; break; 205 | case "midi": r = "audio/midi"; break; 206 | case "mif": r = "application/x-mif"; break; 207 | case "mime": r = "message/rfc822"; break; 208 | case "mjf": r = "audio/x-vnd.audioexplosion.mjuicemediafile"; break; 209 | case "mjpg": r = "video/x-motion-jpeg"; break; 210 | case "mm": r = "application/base64"; break; 211 | case "mme": r = "application/base64"; break; 212 | case "mod": r = "audio/mod"; break; 213 | case "moov": r = "video/quicktime"; break; 214 | case "mov": r = "video/quicktime"; break; 215 | case "movie": r = "video/x-sgi-movie"; break; 216 | case "mp2": r = "audio/mpeg"; break; 217 | case "mp3": r = "audio/mpeg"; break; 218 | case "mpa": r = "audio/mpeg"; break; 219 | case "mpc": r = "application/x-project"; break; 220 | case "mpe": r = "video/mpeg"; break; 221 | case "mpeg": r = "video/mpeg"; break; 222 | case "mpg": r = "video/mpeg"; break; 223 | case "mpga": r = "audio/mpeg"; break; 224 | case "mpp": r = "application/vnd.ms-project"; break; 225 | case "mpt": r = "application/vnd.ms-project"; break; 226 | case "mpv": r = "application/vnd.ms-project"; break; 227 | case "mpx": r = "application/vnd.ms-project"; break; 228 | case "mrc": r = "application/marc"; break; 229 | case "ms": r = "application/x-troff-ms"; break; 230 | case "mv": r = "video/x-sgi-movie"; break; 231 | case "my": r = "audio/make"; break; 232 | case "mzz": r = "application/x-vnd.audioexplosion.mzz"; break; 233 | case "nap": r = "image/naplps"; break; 234 | case "naplps": r = "image/naplps"; break; 235 | case "nc": r = "application/x-netcdf"; break; 236 | case "ncm": r = "application/vnd.nokia.configuration-message"; break; 237 | case "nif": r = "image/x-niff"; break; 238 | case "niff": r = "image/x-niff"; break; 239 | case "nix": r = "application/x-mix-transfer"; break; 240 | case "nsc": r = "application/x-conference"; break; 241 | case "nvd": r = "application/x-navidoc"; break; 242 | case "o": r = "application/octet-stream"; break; 243 | case "oda": r = "application/oda"; break; 244 | case "omc": r = "application/x-omc"; break; 245 | case "omcd": r = "application/x-omcdatamaker"; break; 246 | case "omcr": r = "application/x-omcregerator"; break; 247 | case "p": r = "text/x-pascal"; break; 248 | case "p10": r = "application/pkcs10"; break; 249 | case "p12": r = "application/pkcs-12"; break; 250 | case "p7a": r = "application/x-pkcs7-signature"; break; 251 | case "p7c": r = "application/pkcs7-mime"; break; 252 | case "p7m": r = "application/pkcs7-mime"; break; 253 | case "p7r": r = "application/x-pkcs7-certreqresp"; break; 254 | case "p7s": r = "application/pkcs7-signature"; break; 255 | case "part": r = "application/pro_eng"; break; 256 | case "pas": r = "text/pascal"; break; 257 | case "pbm": r = "image/x-portable-bitmap"; break; 258 | case "pcl": r = "application/vnd.hp-pcl"; break; 259 | case "pct": r = "image/x-pict"; break; 260 | case "pcx": r = "image/x-pcx"; break; 261 | case "pdb": r = "chemical/x-pdb"; break; 262 | case "pdf": r = "application/pdf"; break; 263 | case "pfunk": r = "audio/make"; break; 264 | case "pgm": r = "image/x-portable-greymap"; break; 265 | case "pic": r = "image/pict"; break; 266 | case "pict": r = "image/pict"; break; 267 | case "pkg": r = "application/x-newton-compatible-pkg"; break; 268 | case "pko": r = "application/vnd.ms-pki.pko"; break; 269 | case "pl": r = "text/plain"; break; 270 | case "plx": r = "application/x-pixclscript"; break; 271 | case "pm": r = "image/x-xpixmap"; break; 272 | case "pm4": r = "application/x-pagemaker"; break; 273 | case "pm5": r = "application/x-pagemaker"; break; 274 | case "png": r = "image/png"; break; 275 | case "pnm": r = "application/x-portable-anymap"; break; 276 | case "pot": r = "application/vnd.ms-powerpoint"; break; 277 | case "pov": r = "model/x-pov"; break; 278 | case "ppa": r = "application/vnd.ms-powerpoint"; break; 279 | case "ppm": r = "image/x-portable-pixmap"; break; 280 | case "pps": r = "application/vnd.ms-powerpoint"; break; 281 | case "ppt": r = "application/vnd.ms-powerpoint"; break; 282 | case "ppz": r = "application/vnd.ms-powerpoint"; break; 283 | case "pre": r = "application/x-freelance"; break; 284 | case "prt": r = "application/pro_eng"; break; 285 | case "ps": r = "application/postscript"; break; 286 | case "psd": r = "application/octet-stream"; break; 287 | case "pvu": r = "paleovu/x-pv"; break; 288 | case "pwz": r = "application/vnd.ms-powerpoint"; break; 289 | case "py": r = "text/x-script.phyton"; break; 290 | case "pyc": r = "applicaiton/x-bytecode.python"; break; 291 | case "qcp": r = "audio/vnd.qcelp"; break; 292 | case "qd3": r = "x-world/x-3dmf"; break; 293 | case "qd3d": r = "x-world/x-3dmf"; break; 294 | case "qif": r = "image/x-quicktime"; break; 295 | case "qt": r = "video/quicktime"; break; 296 | case "qtc": r = "video/x-qtc"; break; 297 | case "qti": r = "image/x-quicktime"; break; 298 | case "qtif": r = "image/x-quicktime"; break; 299 | case "ra": r = "audio/x-pn-realaudio"; break; 300 | case "ram": r = "audio/x-pn-realaudio"; break; 301 | case "ras": r = "application/x-cmu-raster"; break; 302 | case "rast": r = "image/cmu-raster"; break; 303 | case "rexx": r = "text/x-script.rexx"; break; 304 | case "rf": r = "image/vnd.rn-realflash"; break; 305 | case "rgb": r = "image/x-rgb"; break; 306 | case "rm": r = "application/vnd.rn-realmedia"; break; 307 | case "rmi": r = "audio/mid"; break; 308 | case "rmm": r = "audio/x-pn-realaudio"; break; 309 | case "rmp": r = "audio/x-pn-realaudio"; break; 310 | case "rng": r = "application/ringing-tones"; break; 311 | case "rnx": r = "application/vnd.rn-realplayer"; break; 312 | case "roff": r = "application/x-troff"; break; 313 | case "rp": r = "image/vnd.rn-realpix"; break; 314 | case "rpm": r = "audio/x-pn-realaudio-plugin"; break; 315 | case "rt": r = "text/richtext"; break; 316 | case "rtf": r = "text/richtext"; break; 317 | case "rtx": r = "text/richtext"; break; 318 | case "rv": r = "video/vnd.rn-realvideo"; break; 319 | case "s": r = "text/x-asm"; break; 320 | case "s3m": r = "audio/s3m"; break; 321 | case "saveme": r = "application/octet-stream"; break; 322 | case "sbk": r = "application/x-tbook"; break; 323 | case "scm": r = "application/x-lotusscreencam"; break; 324 | case "sdml": r = "text/plain"; break; 325 | case "sdp": r = "application/sdp"; break; 326 | case "sdr": r = "application/sounder"; break; 327 | case "sea": r = "application/sea"; break; 328 | case "set": r = "application/set"; break; 329 | case "sgm": r = "text/sgml"; break; 330 | case "sgml": r = "text/sgml"; break; 331 | case "sh": r = "application/x-sh"; break; 332 | case "shar": r = "application/x-shar"; break; 333 | case "shtml": r = "text/html"; break; 334 | case "sid": r = "audio/x-psid"; break; 335 | case "sit": r = "application/x-sit"; break; 336 | case "skd": r = "application/x-koan"; break; 337 | case "skm": r = "application/x-koan"; break; 338 | case "skp": r = "application/x-koan"; break; 339 | case "skt": r = "application/x-koan"; break; 340 | case "sl": r = "application/x-seelogo"; break; 341 | case "smi": r = "application/smil"; break; 342 | case "smil": r = "application/smil"; break; 343 | case "snd": r = "audio/basic"; break; 344 | case "sol": r = "application/solids"; break; 345 | case "spc": r = "text/x-speech"; break; 346 | case "spl": r = "application/futuresplash"; break; 347 | case "spr": r = "application/x-sprite"; break; 348 | case "sprite": r = "application/x-sprite"; break; 349 | case "src": r = "application/x-wais-source"; break; 350 | case "ssi": r = "text/x-server-parsed-html"; break; 351 | case "ssm": r = "application/streamingmedia"; break; 352 | case "sst": r = "application/vnd.ms-pki.certstore"; break; 353 | case "step": r = "application/step"; break; 354 | case "stl": r = "application/sla"; break; 355 | case "stp": r = "application/step"; break; 356 | case "sv4cpio": r = "application/x-sv4cpio"; break; 357 | case "sv4crc": r = "application/x-sv4crc"; break; 358 | case "svf": r = "image/vnd.dwg"; break; 359 | case "svr": r = "application/x-world"; break; 360 | case "swf": r = "application/x-shockwave-flash"; break; 361 | case "t": r = "application/x-troff"; break; 362 | case "talk": r = "text/x-speech"; break; 363 | case "tar": r = "application/x-tar"; break; 364 | case "tbk": r = "application/toolbook"; break; 365 | case "tcl": r = "application/x-tcl"; break; 366 | case "tcsh": r = "text/x-script.tcsh"; break; 367 | case "tex": r = "application/x-tex"; break; 368 | case "texi": r = "application/x-texinfo"; break; 369 | case "texinfo": r = "application/x-texinfo"; break; 370 | case "text": r = "text/plain"; break; 371 | case "tgz": r = "application/x-compressed"; break; 372 | case "tif": r = "image/tiff"; break; 373 | case "tiff": r = "image/tiff"; break; 374 | case "tr": r = "application/x-troff"; break; 375 | case "tsi": r = "audio/tsp-audio"; break; 376 | case "tsp": r = "application/dsptype"; break; 377 | case "tsv": r = "text/tab-separated-values"; break; 378 | case "turbot": r = "image/florian"; break; 379 | case "txt": r = "text/plain"; break; 380 | case "uil": r = "text/x-uil"; break; 381 | case "uni": r = "text/uri-list"; break; 382 | case "unis": r = "text/uri-list"; break; 383 | case "unv": r = "application/i-deas"; break; 384 | case "uri": r = "text/uri-list"; break; 385 | case "uris": r = "text/uri-list"; break; 386 | case "ustar": r = "application/x-ustar"; break; 387 | case "uu": r = "application/octet-stream"; break; 388 | case "uue": r = "text/x-uuencode"; break; 389 | case "vcd": r = "application/x-cdlink"; break; 390 | case "vcs": r = "text/x-vcalendar"; break; 391 | case "vda": r = "application/vda"; break; 392 | case "vdo": r = "video/vdo"; break; 393 | case "vew": r = "application/groupwise"; break; 394 | case "viv": r = "video/vivo"; break; 395 | case "vivo": r = "video/vivo"; break; 396 | case "vmd": r = "application/vocaltec-media-desc"; break; 397 | case "vmf": r = "application/vocaltec-media-file"; break; 398 | case "voc": r = "audio/voc"; break; 399 | case "vos": r = "video/vosaic"; break; 400 | case "vox": r = "audio/voxware"; break; 401 | case "vqe": r = "audio/x-twinvq-plugin"; break; 402 | case "vqf": r = "audio/x-twinvq"; break; 403 | case "vql": r = "audio/x-twinvq-plugin"; break; 404 | case "vrml": r = "application/x-vrml"; break; 405 | case "vrt": r = "x-world/x-vrt"; break; 406 | case "vsd": r = "application/x-visio"; break; 407 | case "vst": r = "application/x-visio"; break; 408 | case "vsw": r = "application/x-visio"; break; 409 | case "w60": r = "application/wordperfect6.0"; break; 410 | case "w61": r = "application/wordperfect6.1"; break; 411 | case "w6w": r = "application/msword"; break; 412 | case "wav": r = "audio/wav"; break; 413 | case "wb1": r = "application/x-qpro"; break; 414 | case "wbmp": r = "image/vnd.wap.wbmp"; break; 415 | case "web": r = "application/vnd.xara"; break; 416 | case "wiz": r = "application/msword"; break; 417 | case "wk1": r = "application/x-123"; break; 418 | case "wmf": r = "windows/metafile"; break; 419 | case "wml": r = "text/vnd.wap.wml"; break; 420 | case "wmlc": r = "application/vnd.wap.wmlc"; break; 421 | case "wmls": r = "text/vnd.wap.wmlscript"; break; 422 | case "wmlsc": r = "application/vnd.wap.wmlscriptc"; break; 423 | case "word": r = "application/msword"; break; 424 | case "wp": r = "application/wordperfect"; break; 425 | case "wp5": r = "application/wordperfect"; break; 426 | case "wp6": r = "application/wordperfect"; break; 427 | case "wpd": r = "application/wordperfect"; break; 428 | case "wq1": r = "application/x-lotus"; break; 429 | case "wri": r = "application/mswrite"; break; 430 | case "wrl": r = "application/x-world"; break; 431 | case "wrz": r = "x-world/x-vrml"; break; 432 | case "wsc": r = "text/scriplet"; break; 433 | case "wsrc": r = "application/x-wais-source"; break; 434 | case "wtk": r = "application/x-wintalk"; break; 435 | case "xbm": r = "image/x-xbitmap"; break; 436 | case "xdr": r = "video/x-amt-demorun"; break; 437 | case "xgz": r = "xgl/drawing"; break; 438 | case "xif": r = "image/vnd.xiff"; break; 439 | case "xl": r = "application/excel"; break; 440 | case "xla": r = "application/vnd.ms-excel"; break; 441 | case "xlb": r = "application/vnd.ms-excel"; break; 442 | case "xlc": r = "application/vnd.ms-excel"; break; 443 | case "xld": r = "application/vnd.ms-excel"; break; 444 | case "xlk": r = "application/vnd.ms-excel"; break; 445 | case "xll": r = "application/vnd.ms-excel"; break; 446 | case "xlm": r = "application/vnd.ms-excel"; break; 447 | case "xls": r = "application/vnd.ms-excel"; break; 448 | case "xlt": r = "application/vnd.ms-excel"; break; 449 | case "xlv": r = "application/vnd.ms-excel"; break; 450 | case "xlw": r = "application/vnd.ms-excel"; break; 451 | case "xm": r = "audio/xm"; break; 452 | case "xml": r = "application/xml"; break; 453 | case "xmz": r = "xgl/movie"; break; 454 | case "xpix": r = "application/x-vnd.ls-xpix"; break; 455 | case "xpm": r = "image/xpm"; break; 456 | case "x-png": r = "image/png"; break; 457 | case "xsr": r = "video/x-amt-showrun"; break; 458 | case "xwd": r = "image/x-xwd"; break; 459 | case "xyz": r = "chemical/x-pdb"; break; 460 | case "z": r = "application/x-compressed"; break; 461 | case "zip": r = "application/zip"; break; 462 | case "zoo": r = "application/octet-stream"; break; 463 | case "zsh": r = "text/x-script.zsh"; break; 464 | default: r = "application/octet-stream"; break; 465 | } 466 | return r; 467 | } 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /FileDB/Helper/Range.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace Numeria.IO 9 | { 10 | internal class Range 11 | { 12 | public TStart Start { get; set; } 13 | public TEnd End { get; set; } 14 | 15 | public Range() 16 | { 17 | } 18 | 19 | public Range(TStart start, TEnd end) 20 | { 21 | this.Start = start; 22 | this.End = end; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /FileDB/Helper/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Numeria.IO 7 | { 8 | internal static class StringExtensions 9 | { 10 | public static byte[] ToBytes(this string str, int size) 11 | { 12 | if (string.IsNullOrEmpty(str)) 13 | return new byte[size]; 14 | 15 | var buffer = new byte[size]; 16 | var strbytes = Encoding.UTF8.GetBytes(str); 17 | 18 | Array.Copy(strbytes, buffer, size > strbytes.Length ? strbytes.Length : size); 19 | 20 | return buffer; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /FileDB/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FileDB")] 9 | [assembly: AssemblyDescription("FileDB - A file database to store files")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FileDB")] 13 | [assembly: AssemblyCopyright("Copyright © Numeria Informática - 2011")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("54389999-497d-427b-89a1-0ddf381daf74")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.2.0")] 36 | [assembly: AssemblyFileVersion("1.0.2.0")] 37 | -------------------------------------------------------------------------------- /FileDB/Stream/FileDBStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Numeria.IO 5 | { 6 | public sealed class FileDBStream : Stream 7 | { 8 | private Engine _engine = null; 9 | private readonly long _streamLength = 0; 10 | 11 | private long _streamPosition = 0; 12 | private DataPage _currentPage = null; 13 | private int _positionInPage = 0; 14 | private EntryInfo _info = null; 15 | 16 | internal FileDBStream(Engine engine, Guid id) 17 | { 18 | _engine = engine; 19 | 20 | var indexNode = _engine.Search(id); 21 | if (indexNode != null) 22 | { 23 | _streamLength = indexNode.FileLength; 24 | _currentPage = PageFactory.GetDataPage(indexNode.DataPageID, engine.Reader, false); 25 | _info = new EntryInfo(indexNode); 26 | } 27 | } 28 | 29 | /// 30 | /// Get file information 31 | /// 32 | public EntryInfo FileInfo 33 | { 34 | get 35 | { 36 | return _info; 37 | } 38 | } 39 | 40 | public override bool CanRead 41 | { 42 | get { return true; } 43 | } 44 | 45 | public override bool CanSeek 46 | { 47 | get { return false; } 48 | } 49 | 50 | public override bool CanWrite 51 | { 52 | get { return false; } 53 | } 54 | 55 | public override void Flush() 56 | { 57 | throw new NotSupportedException(); 58 | } 59 | 60 | public override long Length 61 | { 62 | get { return _streamLength; } 63 | } 64 | 65 | public override long Position 66 | { 67 | get 68 | { 69 | return _streamPosition; 70 | } 71 | set 72 | { 73 | throw new NotSupportedException(); 74 | } 75 | } 76 | 77 | public override int Read(byte[] buffer, int offset, int count) 78 | { 79 | int bytesLeft = count; 80 | 81 | while (_currentPage != null && bytesLeft > 0) 82 | { 83 | int bytesToCopy = Math.Min(bytesLeft, _currentPage.DataBlockLength - _positionInPage); 84 | Buffer.BlockCopy(_currentPage.DataBlock, _positionInPage, buffer, offset, bytesToCopy); 85 | 86 | _positionInPage += bytesToCopy; 87 | bytesLeft -= bytesToCopy; 88 | offset += bytesToCopy; 89 | _streamPosition += bytesToCopy; 90 | 91 | if (_positionInPage >= _currentPage.DataBlockLength) 92 | { 93 | _positionInPage = 0; 94 | 95 | if (_currentPage.NextPageID == uint.MaxValue) 96 | _currentPage = null; 97 | else 98 | _currentPage = PageFactory.GetDataPage(_currentPage.NextPageID, _engine.Reader, false); 99 | } 100 | } 101 | 102 | return count - bytesLeft; 103 | } 104 | 105 | public override long Seek(long offset, SeekOrigin origin) 106 | { 107 | throw new NotSupportedException(); 108 | } 109 | 110 | public override void SetLength(long value) 111 | { 112 | throw new NotSupportedException(); 113 | } 114 | 115 | public override void Write(byte[] buffer, int offset, int count) 116 | { 117 | throw new NotSupportedException(); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /FileDB/Structure/BasePage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal enum PageType 10 | { 11 | /// 12 | /// Data = 1 13 | /// 14 | Data = 1, 15 | 16 | /// 17 | /// Index = 2 18 | /// 19 | Index = 2 20 | } 21 | 22 | internal abstract class BasePage 23 | { 24 | public const long PAGE_SIZE = 4096; 25 | 26 | public uint PageID { get; set; } 27 | public abstract PageType Type { get; } 28 | public uint NextPageID { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /FileDB/Structure/DataPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class DataPage : BasePage 10 | { 11 | public const long HEADER_SIZE = 8; 12 | public const long DATA_PER_PAGE = 4088; 13 | 14 | public override PageType Type { get { return PageType.Data; } } // 1 byte 15 | 16 | public bool IsEmpty { get; set; } // 1 byte 17 | public short DataBlockLength { get; set; } // 2 bytes 18 | 19 | public byte[] DataBlock { get; set; } 20 | 21 | public DataPage(uint pageID) 22 | { 23 | PageID = pageID; 24 | IsEmpty = true; 25 | DataBlockLength = 0; 26 | NextPageID = uint.MaxValue; 27 | DataBlock = new byte[DataPage.DATA_PER_PAGE]; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /FileDB/Structure/EntryInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | public class EntryInfo 10 | { 11 | private Guid _id; 12 | private string _fileName; 13 | private uint _fileLength; 14 | private string _mimeType; 15 | 16 | public Guid ID { get { return _id; } } 17 | public string FileName { get { return _fileName; } } 18 | public uint FileLength { get { return _fileLength; } internal set { _fileLength = value; } } 19 | public string MimeType { get { return _mimeType; } } 20 | 21 | internal EntryInfo(string fileName) 22 | { 23 | _id = Guid.NewGuid(); 24 | _fileName = Path.GetFileName(fileName); 25 | _mimeType = MimeTypeConverter.Convert(Path.GetExtension(_fileName)); 26 | _fileLength = 0; 27 | } 28 | 29 | internal EntryInfo(IndexNode node) 30 | { 31 | _id = node.ID; 32 | _fileName = node.FileName + "." + node.FileExtension; 33 | _mimeType = MimeTypeConverter.Convert(node.FileExtension); 34 | _fileLength = node.FileLength; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /FileDB/Structure/Header.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class Header 10 | { 11 | public const long LOCKER_POS = 98; 12 | public const long HEADER_SIZE = 100; 13 | 14 | public const string FileID = "FileDB"; // 6 bytes 15 | public const short FileVersion = 1; // 2 bytes 16 | 17 | /// 18 | /// Armazena a primeira página que contem o inicio do indice. Valor sempre fixo = 0. Utilizado o inicio da busca binária 19 | /// Storage the fist index page (root page). It's fixed on 0 (zero) 20 | /// 21 | public uint IndexRootPageID { get; set; } // 4 bytes 22 | 23 | /// 24 | /// Contem a página que possui espaço disponível para novas inclusões de indices 25 | /// This last has free nodes to be used 26 | /// 27 | public uint FreeIndexPageID { get; set; } // 4 bytes 28 | 29 | /// 30 | /// Quando há exclusão de dados, a primeira pagina a ficar vazia infora a esse ponteiro que depois vai aproveitar numa proxima inclusão 31 | /// When a deleted data, this variable point to first page emtpy. I will use to insert the next data page 32 | /// 33 | public uint FreeDataPageID { get; set; } // 4 bytes 34 | 35 | /// 36 | /// Define, numa exclusão de dados, a ultima pagina excluida. Será utilizado para fazer segmentos continuos de exclusão, ou seja, assim que um segundo arquivo for apagado, o ponteiro inicial dele deve apontar para o ponteiro final do outro 37 | /// Define, in a deleted data, the last deleted page. It's used to make continuos statments of empty page data 38 | /// 39 | public uint LastFreeDataPageID { get; set; } // 4 bytes 40 | 41 | /// 42 | /// Ultima página utilizada pelo FileDB (seja para Indice/Data). É utilizado para quando o arquivo precisa crescer (criar nova pagina) 43 | /// Last used page on FileDB disk (even index or data page). It's used to grow the file db (create new pages) 44 | /// 45 | public uint LastPageID { get; set; } // 4 bytes 46 | 47 | public Header() 48 | { 49 | IndexRootPageID = uint.MaxValue; 50 | FreeIndexPageID = uint.MaxValue; 51 | FreeDataPageID = uint.MaxValue; 52 | LastFreeDataPageID = uint.MaxValue; 53 | LastPageID = uint.MaxValue; 54 | IsDirty = false; 55 | } 56 | 57 | public bool IsDirty { get; set; } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /FileDB/Structure/IndexLink.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class IndexLink 10 | { 11 | public byte Index { get; set; } 12 | public uint PageID { get; set; } 13 | 14 | public IndexLink() 15 | { 16 | Index = 0; 17 | PageID = uint.MaxValue; 18 | } 19 | 20 | public bool IsEmpty 21 | { 22 | get 23 | { 24 | return PageID == uint.MaxValue; 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /FileDB/Structure/IndexNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class IndexNode 10 | { 11 | public const int FILENAME_SIZE = 41; // Size of file name string 12 | public const int FILE_EXTENSION_SIZE = 5; // Size of file extension string 13 | public const int INDEX_NODE_SIZE = 81; // Node Index size 14 | 15 | public Guid ID { get; set; } // 16 bytes 16 | 17 | public bool IsDeleted { get; set; } // 1 byte 18 | 19 | public IndexLink Right { get; set; } // 5 bytes 20 | public IndexLink Left { get; set; } // 5 bytes 21 | 22 | public uint DataPageID { get; set; } // 4 bytes 23 | 24 | // Info 25 | public string FileName { get; set; } // 41 bytes (file name + extension) 26 | public string FileExtension { get; set; } // 5 bytes (only extension without dot ".") 27 | public uint FileLength { get; set; } // 4 bytes 28 | 29 | public IndexPage IndexPage { get; set; } 30 | 31 | public IndexNode(IndexPage indexPage) 32 | { 33 | ID = Guid.Empty; 34 | IsDeleted = true; // Start with index node mark as deleted. Update this after save all stream on disk 35 | Right = new IndexLink(); 36 | Left = new IndexLink(); 37 | DataPageID = uint.MaxValue; 38 | IndexPage = indexPage; 39 | } 40 | 41 | public void UpdateFromEntry(EntryInfo entity) 42 | { 43 | ID = entity.ID; 44 | FileName = Path.GetFileNameWithoutExtension(entity.FileName); 45 | FileExtension = Path.GetExtension(entity.FileName).Replace(".", ""); 46 | FileLength = entity.FileLength; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /FileDB/Structure/IndexPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Numeria.IO 8 | { 9 | internal class IndexPage : BasePage 10 | { 11 | public const long HEADER_SIZE = 46; 12 | public const int NODES_PER_PAGE = 50; 13 | 14 | public override PageType Type { get { return PageType.Index; } } // 1 byte 15 | public byte NodeIndex { get; set; } // 1 byte 16 | 17 | public IndexNode[] Nodes { get; set; } 18 | 19 | public bool IsDirty { get; set; } 20 | 21 | public IndexPage(uint pageID) 22 | { 23 | PageID = pageID; 24 | NextPageID = uint.MaxValue; 25 | NodeIndex = 0; 26 | Nodes = new IndexNode[IndexPage.NODES_PER_PAGE]; 27 | IsDirty = false; 28 | 29 | for (int i = 0; i < IndexPage.NODES_PER_PAGE; i++) 30 | { 31 | var node = Nodes[i] = new IndexNode(this); 32 | } 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Icons/filedb_200h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbdavid/FileDB/64a09820b3da4aaaa62bffcd5ab6aa5afdfe3c51/Icons/filedb_200h.png -------------------------------------------------------------------------------- /Icons/logo-filedb.txt: -------------------------------------------------------------------------------- 1 | http://i.imgur.com/Lasg3.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mauricio David 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MvcTest/Content/fileuploader.css: -------------------------------------------------------------------------------- 1 | .qq-uploader { position:relative; width: 100%;} 2 | 3 | .qq-upload-button { 4 | display:block; /* or inline-block */ 5 | width: 105px; padding: 7px 0; text-align:center; 6 | background:#880000; border-bottom:1px solid #ddd;color:#fff; 7 | } 8 | .qq-upload-button-hover {background:#cc0000;} 9 | .qq-upload-button-focus {outline:1px dotted black;} 10 | 11 | .qq-upload-drop-area { 12 | position:absolute; top:0; left:0; width:100%; height:100%; min-height: 70px; z-index:2; 13 | background:#FF9797; text-align:center; 14 | } 15 | .qq-upload-drop-area span { 16 | display:block; position:absolute; top: 50%; width:100%; margin-top:-8px; font-size:16px; 17 | } 18 | .qq-upload-drop-area-active {background:#FF7171;} 19 | 20 | .qq-upload-list {margin:15px 35px; padding:0; list-style:disc;} 21 | .qq-upload-list li { margin:0; padding:0; line-height:15px; font-size:12px;} 22 | .qq-upload-file, .qq-upload-spinner, .qq-upload-size, .qq-upload-cancel, .qq-upload-failed-text { 23 | margin-right: 7px; 24 | } 25 | 26 | .qq-upload-file {} 27 | .qq-upload-spinner {display:inline-block; background: url("loading.gif"); width:15px; height:15px; vertical-align:text-bottom;} 28 | .qq-upload-size,.qq-upload-cancel {font-size:11px;} 29 | 30 | .qq-upload-failed-text {display:none;} 31 | .qq-upload-fail .qq-upload-failed-text {display:inline;} -------------------------------------------------------------------------------- /MvcTest/Content/fileuploader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * http://github.com/valums/file-uploader 3 | * 4 | * Multiple file upload component with progress-bar, drag-and-drop. 5 | * © 2010 Andrew Valums ( andrew(at)valums.com ) 6 | * 7 | * Licensed under GNU GPL 2 or later, see license.txt. 8 | */ 9 | 10 | // 11 | // Helper functions 12 | // 13 | 14 | var qq = qq || {}; 15 | 16 | /** 17 | * Adds all missing properties from second obj to first obj 18 | */ 19 | qq.extend = function(first, second){ 20 | for (var prop in second){ 21 | first[prop] = second[prop]; 22 | } 23 | }; 24 | 25 | /** 26 | * Searches for a given element in the array, returns -1 if it is not present. 27 | * @param {Number} [from] The index at which to begin the search 28 | */ 29 | qq.indexOf = function(arr, elt, from){ 30 | if (arr.indexOf) return arr.indexOf(elt, from); 31 | 32 | from = from || 0; 33 | var len = arr.length; 34 | 35 | if (from < 0) from += len; 36 | 37 | for (; from < len; from++){ 38 | if (from in arr && arr[from] === elt){ 39 | return from; 40 | } 41 | } 42 | return -1; 43 | }; 44 | 45 | qq.getUniqueId = (function(){ 46 | var id = 0; 47 | return function(){ return id++; }; 48 | })(); 49 | 50 | // 51 | // Events 52 | 53 | qq.attach = function(element, type, fn){ 54 | if (element.addEventListener){ 55 | element.addEventListener(type, fn, false); 56 | } else if (element.attachEvent){ 57 | element.attachEvent('on' + type, fn); 58 | } 59 | }; 60 | qq.detach = function(element, type, fn){ 61 | if (element.removeEventListener){ 62 | element.removeEventListener(type, fn, false); 63 | } else if (element.attachEvent){ 64 | element.detachEvent('on' + type, fn); 65 | } 66 | }; 67 | 68 | qq.preventDefault = function(e){ 69 | if (e.preventDefault){ 70 | e.preventDefault(); 71 | } else{ 72 | e.returnValue = false; 73 | } 74 | }; 75 | 76 | // 77 | // Node manipulations 78 | 79 | /** 80 | * Insert node a before node b. 81 | */ 82 | qq.insertBefore = function(a, b){ 83 | b.parentNode.insertBefore(a, b); 84 | }; 85 | qq.remove = function(element){ 86 | element.parentNode.removeChild(element); 87 | }; 88 | 89 | qq.contains = function(parent, descendant){ 90 | // compareposition returns false in this case 91 | if (parent == descendant) return true; 92 | 93 | if (parent.contains){ 94 | return parent.contains(descendant); 95 | } else { 96 | return !!(descendant.compareDocumentPosition(parent) & 8); 97 | } 98 | }; 99 | 100 | /** 101 | * Creates and returns element from html string 102 | * Uses innerHTML to create an element 103 | */ 104 | qq.toElement = (function(){ 105 | var div = document.createElement('div'); 106 | return function(html){ 107 | div.innerHTML = html; 108 | var element = div.firstChild; 109 | div.removeChild(element); 110 | return element; 111 | }; 112 | })(); 113 | 114 | // 115 | // Node properties and attributes 116 | 117 | /** 118 | * Sets styles for an element. 119 | * Fixes opacity in IE6-8. 120 | */ 121 | qq.css = function(element, styles){ 122 | if (styles.opacity != null){ 123 | if (typeof element.style.opacity != 'string' && typeof(element.filters) != 'undefined'){ 124 | styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')'; 125 | } 126 | } 127 | qq.extend(element.style, styles); 128 | }; 129 | qq.hasClass = function(element, name){ 130 | var re = new RegExp('(^| )' + name + '( |$)'); 131 | return re.test(element.className); 132 | }; 133 | qq.addClass = function(element, name){ 134 | if (!qq.hasClass(element, name)){ 135 | element.className += ' ' + name; 136 | } 137 | }; 138 | qq.removeClass = function(element, name){ 139 | var re = new RegExp('(^| )' + name + '( |$)'); 140 | element.className = element.className.replace(re, ' ').replace(/^\s+|\s+$/g, ""); 141 | }; 142 | qq.setText = function(element, text){ 143 | element.innerText = text; 144 | element.textContent = text; 145 | }; 146 | 147 | // 148 | // Selecting elements 149 | 150 | qq.children = function(element){ 151 | var children = [], 152 | child = element.firstChild; 153 | 154 | while (child){ 155 | if (child.nodeType == 1){ 156 | children.push(child); 157 | } 158 | child = child.nextSibling; 159 | } 160 | 161 | return children; 162 | }; 163 | 164 | qq.getByClass = function(element, className){ 165 | if (element.querySelectorAll){ 166 | return element.querySelectorAll('.' + className); 167 | } 168 | 169 | var result = []; 170 | var candidates = element.getElementsByTagName("*"); 171 | var len = candidates.length; 172 | 173 | for (var i = 0; i < len; i++){ 174 | if (qq.hasClass(candidates[i], className)){ 175 | result.push(candidates[i]); 176 | } 177 | } 178 | return result; 179 | }; 180 | 181 | /** 182 | * obj2url() takes a json-object as argument and generates 183 | * a querystring. pretty much like jQuery.param() 184 | * 185 | * how to use: 186 | * 187 | * `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');` 188 | * 189 | * will result in: 190 | * 191 | * `http://any.url/upload?otherParam=value&a=b&c=d` 192 | * 193 | * @param Object JSON-Object 194 | * @param String current querystring-part 195 | * @return String encoded querystring 196 | */ 197 | qq.obj2url = function(obj, temp, prefixDone){ 198 | var uristrings = [], 199 | prefix = '&', 200 | add = function(nextObj, i){ 201 | var nextTemp = temp 202 | ? (/\[\]$/.test(temp)) // prevent double-encoding 203 | ? temp 204 | : temp+'['+i+']' 205 | : i; 206 | if ((nextTemp != 'undefined') && (i != 'undefined')) { 207 | uristrings.push( 208 | (typeof nextObj === 'object') 209 | ? qq.obj2url(nextObj, nextTemp, true) 210 | : (Object.prototype.toString.call(nextObj) === '[object Function]') 211 | ? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj()) 212 | : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj) 213 | ); 214 | } 215 | }; 216 | 217 | if (!prefixDone && temp) { 218 | prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?'; 219 | uristrings.push(temp); 220 | uristrings.push(qq.obj2url(obj)); 221 | } else if ((Object.prototype.toString.call(obj) === '[object Array]') && (typeof obj != 'undefined') ) { 222 | // we wont use a for-in-loop on an array (performance) 223 | for (var i = 0, len = obj.length; i < len; ++i){ 224 | add(obj[i], i); 225 | } 226 | } else if ((typeof obj != 'undefined') && (obj !== null) && (typeof obj === "object")){ 227 | // for anything else but a scalar, we will use for-in-loop 228 | for (var i in obj){ 229 | add(obj[i], i); 230 | } 231 | } else { 232 | uristrings.push(encodeURIComponent(temp) + '=' + encodeURIComponent(obj)); 233 | } 234 | 235 | return uristrings.join(prefix) 236 | .replace(/^&/, '') 237 | .replace(/%20/g, '+'); 238 | }; 239 | 240 | // 241 | // 242 | // Uploader Classes 243 | // 244 | // 245 | 246 | var qq = qq || {}; 247 | 248 | /** 249 | * Creates upload button, validates upload, but doesn't create file list or dd. 250 | */ 251 | qq.FileUploaderBasic = function(o){ 252 | this._options = { 253 | // set to true to see the server response 254 | debug: false, 255 | action: '/server/upload', 256 | params: {}, 257 | button: null, 258 | multiple: true, 259 | maxConnections: 3, 260 | // validation 261 | allowedExtensions: [], 262 | sizeLimit: 0, 263 | minSizeLimit: 0, 264 | // events 265 | // return false to cancel submit 266 | onSubmit: function(id, fileName){}, 267 | onProgress: function(id, fileName, loaded, total){}, 268 | onComplete: function(id, fileName, responseJSON){}, 269 | onCancel: function(id, fileName){}, 270 | // messages 271 | messages: { 272 | typeError: "{file} has invalid extension. Only {extensions} are allowed.", 273 | sizeError: "{file} is too large, maximum file size is {sizeLimit}.", 274 | minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.", 275 | emptyError: "{file} is empty, please select files again without it.", 276 | onLeave: "The files are being uploaded, if you leave now the upload will be cancelled." 277 | }, 278 | showMessage: function(message){ 279 | alert(message); 280 | } 281 | }; 282 | qq.extend(this._options, o); 283 | 284 | // number of files being uploaded 285 | this._filesInProgress = 0; 286 | this._handler = this._createUploadHandler(); 287 | 288 | if (this._options.button){ 289 | this._button = this._createUploadButton(this._options.button); 290 | } 291 | 292 | this._preventLeaveInProgress(); 293 | }; 294 | 295 | qq.FileUploaderBasic.prototype = { 296 | setParams: function(params){ 297 | this._options.params = params; 298 | }, 299 | getInProgress: function(){ 300 | return this._filesInProgress; 301 | }, 302 | _createUploadButton: function(element){ 303 | var self = this; 304 | 305 | return new qq.UploadButton({ 306 | element: element, 307 | multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(), 308 | onChange: function(input){ 309 | self._onInputChange(input); 310 | } 311 | }); 312 | }, 313 | _createUploadHandler: function(){ 314 | var self = this, 315 | handlerClass; 316 | 317 | if(qq.UploadHandlerXhr.isSupported()){ 318 | handlerClass = 'UploadHandlerXhr'; 319 | } else { 320 | handlerClass = 'UploadHandlerForm'; 321 | } 322 | 323 | var handler = new qq[handlerClass]({ 324 | debug: this._options.debug, 325 | action: this._options.action, 326 | maxConnections: this._options.maxConnections, 327 | onProgress: function(id, fileName, loaded, total){ 328 | self._onProgress(id, fileName, loaded, total); 329 | self._options.onProgress(id, fileName, loaded, total); 330 | }, 331 | onComplete: function(id, fileName, result){ 332 | self._onComplete(id, fileName, result); 333 | self._options.onComplete(id, fileName, result); 334 | }, 335 | onCancel: function(id, fileName){ 336 | self._onCancel(id, fileName); 337 | self._options.onCancel(id, fileName); 338 | } 339 | }); 340 | 341 | return handler; 342 | }, 343 | _preventLeaveInProgress: function(){ 344 | var self = this; 345 | 346 | qq.attach(window, 'beforeunload', function(e){ 347 | if (!self._filesInProgress){return;} 348 | 349 | var e = e || window.event; 350 | // for ie, ff 351 | e.returnValue = self._options.messages.onLeave; 352 | // for webkit 353 | return self._options.messages.onLeave; 354 | }); 355 | }, 356 | _onSubmit: function(id, fileName){ 357 | this._filesInProgress++; 358 | }, 359 | _onProgress: function(id, fileName, loaded, total){ 360 | }, 361 | _onComplete: function(id, fileName, result){ 362 | this._filesInProgress--; 363 | if (result.error){ 364 | this._options.showMessage(result.error); 365 | } 366 | }, 367 | _onCancel: function(id, fileName){ 368 | this._filesInProgress--; 369 | }, 370 | _onInputChange: function(input){ 371 | if (this._handler instanceof qq.UploadHandlerXhr){ 372 | this._uploadFileList(input.files); 373 | } else { 374 | if (this._validateFile(input)){ 375 | this._uploadFile(input); 376 | } 377 | } 378 | this._button.reset(); 379 | }, 380 | _uploadFileList: function(files){ 381 | for (var i=0; i this._options.sizeLimit){ 422 | this._error('sizeError', name); 423 | return false; 424 | 425 | } else if (size && size < this._options.minSizeLimit){ 426 | this._error('minSizeError', name); 427 | return false; 428 | } 429 | 430 | return true; 431 | }, 432 | _error: function(code, fileName){ 433 | var message = this._options.messages[code]; 434 | function r(name, replacement){ message = message.replace(name, replacement); } 435 | 436 | r('{file}', this._formatFileName(fileName)); 437 | r('{extensions}', this._options.allowedExtensions.join(', ')); 438 | r('{sizeLimit}', this._formatSize(this._options.sizeLimit)); 439 | r('{minSizeLimit}', this._formatSize(this._options.minSizeLimit)); 440 | 441 | this._options.showMessage(message); 442 | }, 443 | _formatFileName: function(name){ 444 | if (name.length > 33){ 445 | name = name.slice(0, 19) + '...' + name.slice(-13); 446 | } 447 | return name; 448 | }, 449 | _isAllowedExtension: function(fileName){ 450 | var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : ''; 451 | var allowed = this._options.allowedExtensions; 452 | 453 | if (!allowed.length){return true;} 454 | 455 | for (var i=0; i 99); 467 | 468 | return Math.max(bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i]; 469 | } 470 | }; 471 | 472 | 473 | /** 474 | * Class that creates upload widget with drag-and-drop and file list 475 | * @inherits qq.FileUploaderBasic 476 | */ 477 | qq.FileUploader = function(o){ 478 | // call parent constructor 479 | qq.FileUploaderBasic.apply(this, arguments); 480 | 481 | // additional options 482 | qq.extend(this._options, { 483 | element: null, 484 | // if set, will be used instead of qq-upload-list in template 485 | listElement: null, 486 | 487 | template: '
' + 488 | '
Drop files here to upload
' + 489 | '
Upload a file
' + 490 | '
    ' + 491 | '
    ', 492 | 493 | // template for one item in file list 494 | fileTemplate: '
  • ' + 495 | '' + 496 | '' + 497 | '' + 498 | 'Cancel' + 499 | 'Failed' + 500 | '
  • ', 501 | 502 | classes: { 503 | // used to get elements from templates 504 | button: 'qq-upload-button', 505 | drop: 'qq-upload-drop-area', 506 | dropActive: 'qq-upload-drop-area-active', 507 | list: 'qq-upload-list', 508 | 509 | file: 'qq-upload-file', 510 | spinner: 'qq-upload-spinner', 511 | size: 'qq-upload-size', 512 | cancel: 'qq-upload-cancel', 513 | 514 | // added to list item when upload completes 515 | // used in css to hide progress spinner 516 | success: 'qq-upload-success', 517 | fail: 'qq-upload-fail' 518 | } 519 | }); 520 | // overwrite options with user supplied 521 | qq.extend(this._options, o); 522 | 523 | this._element = this._options.element; 524 | this._element.innerHTML = this._options.template; 525 | this._listElement = this._options.listElement || this._find(this._element, 'list'); 526 | 527 | this._classes = this._options.classes; 528 | 529 | this._button = this._createUploadButton(this._find(this._element, 'button')); 530 | 531 | this._bindCancelEvent(); 532 | this._setupDragDrop(); 533 | }; 534 | 535 | // inherit from Basic Uploader 536 | qq.extend(qq.FileUploader.prototype, qq.FileUploaderBasic.prototype); 537 | 538 | qq.extend(qq.FileUploader.prototype, { 539 | /** 540 | * Gets one of the elements listed in this._options.classes 541 | **/ 542 | _find: function(parent, type){ 543 | var element = qq.getByClass(parent, this._options.classes[type])[0]; 544 | if (!element){ 545 | throw new Error('element not found ' + type); 546 | } 547 | 548 | return element; 549 | }, 550 | _setupDragDrop: function(){ 551 | var self = this, 552 | dropArea = this._find(this._element, 'drop'); 553 | 554 | var dz = new qq.UploadDropZone({ 555 | element: dropArea, 556 | onEnter: function(e){ 557 | qq.addClass(dropArea, self._classes.dropActive); 558 | e.stopPropagation(); 559 | }, 560 | onLeave: function(e){ 561 | e.stopPropagation(); 562 | }, 563 | onLeaveNotDescendants: function(e){ 564 | qq.removeClass(dropArea, self._classes.dropActive); 565 | }, 566 | onDrop: function(e){ 567 | dropArea.style.display = 'none'; 568 | qq.removeClass(dropArea, self._classes.dropActive); 569 | self._uploadFileList(e.dataTransfer.files); 570 | } 571 | }); 572 | 573 | dropArea.style.display = 'none'; 574 | 575 | qq.attach(document, 'dragenter', function(e){ 576 | if (!dz._isValidFileDrag(e)) return; 577 | 578 | dropArea.style.display = 'block'; 579 | }); 580 | qq.attach(document, 'dragleave', function(e){ 581 | if (!dz._isValidFileDrag(e)) return; 582 | 583 | var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); 584 | // only fire when leaving document out 585 | if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){ 586 | dropArea.style.display = 'none'; 587 | } 588 | }); 589 | }, 590 | _onSubmit: function(id, fileName){ 591 | qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments); 592 | this._addToList(id, fileName); 593 | }, 594 | _onProgress: function(id, fileName, loaded, total){ 595 | qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments); 596 | 597 | var item = this._getItemByFileId(id); 598 | var size = this._find(item, 'size'); 599 | size.style.display = 'inline'; 600 | 601 | var text; 602 | if (loaded != total){ 603 | text = Math.round(loaded / total * 100) + '% from ' + this._formatSize(total); 604 | } else { 605 | text = this._formatSize(total); 606 | } 607 | 608 | qq.setText(size, text); 609 | }, 610 | _onComplete: function(id, fileName, result){ 611 | qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments); 612 | 613 | // mark completed 614 | var item = this._getItemByFileId(id); 615 | qq.remove(this._find(item, 'cancel')); 616 | qq.remove(this._find(item, 'spinner')); 617 | 618 | if (result.success){ 619 | qq.addClass(item, this._classes.success); 620 | } else { 621 | qq.addClass(item, this._classes.fail); 622 | } 623 | }, 624 | _addToList: function(id, fileName){ 625 | var item = qq.toElement(this._options.fileTemplate); 626 | item.qqFileId = id; 627 | 628 | var fileElement = this._find(item, 'file'); 629 | qq.setText(fileElement, this._formatFileName(fileName)); 630 | this._find(item, 'size').style.display = 'none'; 631 | 632 | this._listElement.appendChild(item); 633 | }, 634 | _getItemByFileId: function(id){ 635 | var item = this._listElement.firstChild; 636 | 637 | // there can't be txt nodes in dynamically created list 638 | // and we can use nextSibling 639 | while (item){ 640 | if (item.qqFileId == id) return item; 641 | item = item.nextSibling; 642 | } 643 | }, 644 | /** 645 | * delegate click event for cancel link 646 | **/ 647 | _bindCancelEvent: function(){ 648 | var self = this, 649 | list = this._listElement; 650 | 651 | qq.attach(list, 'click', function(e){ 652 | e = e || window.event; 653 | var target = e.target || e.srcElement; 654 | 655 | if (qq.hasClass(target, self._classes.cancel)){ 656 | qq.preventDefault(e); 657 | 658 | var item = target.parentNode; 659 | self._handler.cancel(item.qqFileId); 660 | qq.remove(item); 661 | } 662 | }); 663 | } 664 | }); 665 | 666 | qq.UploadDropZone = function(o){ 667 | this._options = { 668 | element: null, 669 | onEnter: function(e){}, 670 | onLeave: function(e){}, 671 | // is not fired when leaving element by hovering descendants 672 | onLeaveNotDescendants: function(e){}, 673 | onDrop: function(e){} 674 | }; 675 | qq.extend(this._options, o); 676 | 677 | this._element = this._options.element; 678 | 679 | this._disableDropOutside(); 680 | this._attachEvents(); 681 | }; 682 | 683 | qq.UploadDropZone.prototype = { 684 | _disableDropOutside: function(e){ 685 | // run only once for all instances 686 | if (!qq.UploadDropZone.dropOutsideDisabled ){ 687 | 688 | qq.attach(document, 'dragover', function(e){ 689 | if (e.dataTransfer){ 690 | e.dataTransfer.dropEffect = 'none'; 691 | e.preventDefault(); 692 | } 693 | }); 694 | 695 | qq.UploadDropZone.dropOutsideDisabled = true; 696 | } 697 | }, 698 | _attachEvents: function(){ 699 | var self = this; 700 | 701 | qq.attach(self._element, 'dragover', function(e){ 702 | if (!self._isValidFileDrag(e)) return; 703 | 704 | var effect = e.dataTransfer.effectAllowed; 705 | if (effect == 'move' || effect == 'linkMove'){ 706 | e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed) 707 | } else { 708 | e.dataTransfer.dropEffect = 'copy'; // for Chrome 709 | } 710 | 711 | e.stopPropagation(); 712 | e.preventDefault(); 713 | }); 714 | 715 | qq.attach(self._element, 'dragenter', function(e){ 716 | if (!self._isValidFileDrag(e)) return; 717 | 718 | self._options.onEnter(e); 719 | }); 720 | 721 | qq.attach(self._element, 'dragleave', function(e){ 722 | if (!self._isValidFileDrag(e)) return; 723 | 724 | self._options.onLeave(e); 725 | 726 | var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); 727 | // do not fire when moving a mouse over a descendant 728 | if (qq.contains(this, relatedTarget)) return; 729 | 730 | self._options.onLeaveNotDescendants(e); 731 | }); 732 | 733 | qq.attach(self._element, 'drop', function(e){ 734 | if (!self._isValidFileDrag(e)) return; 735 | 736 | e.preventDefault(); 737 | self._options.onDrop(e); 738 | }); 739 | }, 740 | _isValidFileDrag: function(e){ 741 | var dt = e.dataTransfer, 742 | // do not check dt.types.contains in webkit, because it crashes safari 4 743 | isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1; 744 | 745 | // dt.effectAllowed is none in Safari 5 746 | // dt.types.contains check is for firefox 747 | return dt && dt.effectAllowed != 'none' && 748 | (dt.files || (!isWebkit && dt.types.contains && dt.types.contains('Files'))); 749 | 750 | } 751 | }; 752 | 753 | qq.UploadButton = function(o){ 754 | this._options = { 755 | element: null, 756 | // if set to true adds multiple attribute to file input 757 | multiple: false, 758 | // name attribute of file input 759 | name: 'file', 760 | onChange: function(input){}, 761 | hoverClass: 'qq-upload-button-hover', 762 | focusClass: 'qq-upload-button-focus' 763 | }; 764 | 765 | qq.extend(this._options, o); 766 | 767 | this._element = this._options.element; 768 | 769 | // make button suitable container for input 770 | qq.css(this._element, { 771 | position: 'relative', 772 | overflow: 'hidden', 773 | // Make sure browse button is in the right side 774 | // in Internet Explorer 775 | direction: 'ltr' 776 | }); 777 | 778 | this._input = this._createInput(); 779 | }; 780 | 781 | qq.UploadButton.prototype = { 782 | /* returns file input element */ 783 | getInput: function(){ 784 | return this._input; 785 | }, 786 | /* cleans/recreates the file input */ 787 | reset: function(){ 788 | if (this._input.parentNode){ 789 | qq.remove(this._input); 790 | } 791 | 792 | qq.removeClass(this._element, this._options.focusClass); 793 | this._input = this._createInput(); 794 | }, 795 | _createInput: function(){ 796 | var input = document.createElement("input"); 797 | 798 | if (this._options.multiple){ 799 | input.setAttribute("multiple", "multiple"); 800 | } 801 | 802 | input.setAttribute("type", "file"); 803 | input.setAttribute("name", this._options.name); 804 | 805 | qq.css(input, { 806 | position: 'absolute', 807 | // in Opera only 'browse' button 808 | // is clickable and it is located at 809 | // the right side of the input 810 | right: 0, 811 | top: 0, 812 | fontFamily: 'Arial', 813 | // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118 814 | fontSize: '118px', 815 | margin: 0, 816 | padding: 0, 817 | cursor: 'pointer', 818 | opacity: 0 819 | }); 820 | 821 | this._element.appendChild(input); 822 | 823 | var self = this; 824 | qq.attach(input, 'change', function(){ 825 | self._options.onChange(input); 826 | }); 827 | 828 | qq.attach(input, 'mouseover', function(){ 829 | qq.addClass(self._element, self._options.hoverClass); 830 | }); 831 | qq.attach(input, 'mouseout', function(){ 832 | qq.removeClass(self._element, self._options.hoverClass); 833 | }); 834 | qq.attach(input, 'focus', function(){ 835 | qq.addClass(self._element, self._options.focusClass); 836 | }); 837 | qq.attach(input, 'blur', function(){ 838 | qq.removeClass(self._element, self._options.focusClass); 839 | }); 840 | 841 | // IE and Opera, unfortunately have 2 tab stops on file input 842 | // which is unacceptable in our case, disable keyboard access 843 | if (window.attachEvent){ 844 | // it is IE or Opera 845 | input.setAttribute('tabIndex', "-1"); 846 | } 847 | 848 | return input; 849 | } 850 | }; 851 | 852 | /** 853 | * Class for uploading files, uploading itself is handled by child classes 854 | */ 855 | qq.UploadHandlerAbstract = function(o){ 856 | this._options = { 857 | debug: false, 858 | action: '/upload.php', 859 | // maximum number of concurrent uploads 860 | maxConnections: 999, 861 | onProgress: function(id, fileName, loaded, total){}, 862 | onComplete: function(id, fileName, response){}, 863 | onCancel: function(id, fileName){} 864 | }; 865 | qq.extend(this._options, o); 866 | 867 | this._queue = []; 868 | // params for files in queue 869 | this._params = []; 870 | }; 871 | qq.UploadHandlerAbstract.prototype = { 872 | log: function(str){ 873 | if (this._options.debug && window.console) console.log('[uploader] ' + str); 874 | }, 875 | /** 876 | * Adds file or file input to the queue 877 | * @returns id 878 | **/ 879 | add: function(file){}, 880 | /** 881 | * Sends the file identified by id and additional query params to the server 882 | */ 883 | upload: function(id, params){ 884 | var len = this._queue.push(id); 885 | 886 | var copy = {}; 887 | qq.extend(copy, params); 888 | this._params[id] = copy; 889 | 890 | // if too many active uploads, wait... 891 | if (len <= this._options.maxConnections){ 892 | this._upload(id, this._params[id]); 893 | } 894 | }, 895 | /** 896 | * Cancels file upload by id 897 | */ 898 | cancel: function(id){ 899 | this._cancel(id); 900 | this._dequeue(id); 901 | }, 902 | /** 903 | * Cancells all uploads 904 | */ 905 | cancelAll: function(){ 906 | for (var i=0; i= max){ 944 | var nextId = this._queue[max-1]; 945 | this._upload(nextId, this._params[nextId]); 946 | } 947 | } 948 | }; 949 | 950 | /** 951 | * Class for uploading files using form and iframe 952 | * @inherits qq.UploadHandlerAbstract 953 | */ 954 | qq.UploadHandlerForm = function(o){ 955 | qq.UploadHandlerAbstract.apply(this, arguments); 956 | 957 | this._inputs = {}; 958 | }; 959 | // @inherits qq.UploadHandlerAbstract 960 | qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype); 961 | 962 | qq.extend(qq.UploadHandlerForm.prototype, { 963 | add: function(fileInput){ 964 | fileInput.setAttribute('name', 'qqfile'); 965 | var id = 'qq-upload-handler-iframe' + qq.getUniqueId(); 966 | 967 | this._inputs[id] = fileInput; 968 | 969 | // remove file input from DOM 970 | if (fileInput.parentNode){ 971 | qq.remove(fileInput); 972 | } 973 | 974 | return id; 975 | }, 976 | getName: function(id){ 977 | // get input value and remove path to normalize 978 | return this._inputs[id].value.replace(/.*(\/|\\)/, ""); 979 | }, 980 | _cancel: function(id){ 981 | this._options.onCancel(id, this.getName(id)); 982 | 983 | delete this._inputs[id]; 984 | 985 | var iframe = document.getElementById(id); 986 | if (iframe){ 987 | // to cancel request set src to something else 988 | // we use src="javascript:false;" because it doesn't 989 | // trigger ie6 prompt on https 990 | iframe.setAttribute('src', 'javascript:false;'); 991 | 992 | qq.remove(iframe); 993 | } 994 | }, 995 | _upload: function(id, params){ 996 | var input = this._inputs[id]; 997 | 998 | if (!input){ 999 | throw new Error('file with passed id was not added, or already uploaded or cancelled'); 1000 | } 1001 | 1002 | var fileName = this.getName(id); 1003 | 1004 | var iframe = this._createIframe(id); 1005 | var form = this._createForm(iframe, params); 1006 | form.appendChild(input); 1007 | 1008 | var self = this; 1009 | this._attachLoadEvent(iframe, function(){ 1010 | self.log('iframe loaded'); 1011 | 1012 | var response = self._getIframeContentJSON(iframe); 1013 | 1014 | self._options.onComplete(id, fileName, response); 1015 | self._dequeue(id); 1016 | 1017 | delete self._inputs[id]; 1018 | // timeout added to fix busy state in FF3.6 1019 | setTimeout(function(){ 1020 | qq.remove(iframe); 1021 | }, 1); 1022 | }); 1023 | 1024 | form.submit(); 1025 | qq.remove(form); 1026 | 1027 | return id; 1028 | }, 1029 | _attachLoadEvent: function(iframe, callback){ 1030 | qq.attach(iframe, 'load', function(){ 1031 | // when we remove iframe from dom 1032 | // the request stops, but in IE load 1033 | // event fires 1034 | if (!iframe.parentNode){ 1035 | return; 1036 | } 1037 | 1038 | // fixing Opera 10.53 1039 | if (iframe.contentDocument && 1040 | iframe.contentDocument.body && 1041 | iframe.contentDocument.body.innerHTML == "false"){ 1042 | // In Opera event is fired second time 1043 | // when body.innerHTML changed from false 1044 | // to server response approx. after 1 sec 1045 | // when we upload file with iframe 1046 | return; 1047 | } 1048 | 1049 | callback(); 1050 | }); 1051 | }, 1052 | /** 1053 | * Returns json object received by iframe from server. 1054 | */ 1055 | _getIframeContentJSON: function(iframe){ 1056 | // iframe.contentWindow.document - for IE<7 1057 | var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document, 1058 | response; 1059 | 1060 | this.log("converting iframe's innerHTML to JSON"); 1061 | this.log("innerHTML = " + doc.body.innerHTML); 1062 | 1063 | try { 1064 | response = eval("(" + doc.body.innerHTML + ")"); 1065 | } catch(err){ 1066 | response = {}; 1067 | } 1068 | 1069 | return response; 1070 | }, 1071 | /** 1072 | * Creates iframe with unique name 1073 | */ 1074 | _createIframe: function(id){ 1075 | // We can't use following code as the name attribute 1076 | // won't be properly registered in IE6, and new window 1077 | // on form submit will open 1078 | // var iframe = document.createElement('iframe'); 1079 | // iframe.setAttribute('name', id); 1080 | 1081 | var iframe = qq.toElement('