Wiki source code of Filesystem Store

Last modified by Vincent Massol on 2025/11/17 17:19

Hide last authors
Michael Hamann 14.1 1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc start="2"/}}
3 {{/box}}
Thomas Mortagne 1.68 4
Michael Hamann 14.1 5 By default, in XWiki the content of attachments and deleted attachments and documents are stored in what we call the "filesystem store". Its default location is ##<permanentdir>/store/file##.
Thomas Mortagne 1.1 6
Michael Hamann 14.1 7 {{version since="17.10.0RC1"}}
8 Despite its name, this "filesystem" store uses whatever [[blob store is configured>>documentation.xs.admin.store.blob.WebHome]] and can thus store attachments and deleted attachments and documents also in other blob stores like S3. The naming of the blobs is independent of the configured blob store.
9 {{/version}}
Thomas Mortagne 1.1 10
Michael Hamann 14.1 11 It's a simple file based storage in which documents and attachments references are (md5) hashed, to avoid problems with various limited (in terms of encoding and path size) file systems/blob stores. The filesystem store implements a two stage commit mechanism to maintain integrity even if the database fails to commit the attachment meta-data for example.
12
Thomas Mortagne 1.70 13 For example the attachment ##XWikiLogo.png## in document ##Sandbox.WebHome## is stored in the following location: ##/1/0/5d42329a923e687f5dff4887d80098/attachments/9/2/0bc685fa0da28168319c0126def81b##.
Thomas Mortagne 1.1 14
Thomas Mortagne 1.26 15 {{box title="And where is my entity located ?"}}
Thomas Mortagne 1.25 16 {{velocity}}
Thomas Mortagne 1.19 17 {{html}}
18 <form>
Thomas Mortagne 9.1 19 <input type="text" name="reference"#if($request.reference) value="$escapetool.xml($request.reference)#end"/>
Thomas Mortagne 1.20 20 <button name="serialize_document">Document</button>
21 <button name="serialize_attachment">Attachment</button>
Thomas Mortagne 1.19 22 </form>
23 {{/html}}
Thomas Mortagne 1.25 24 {{/velocity}}
Thomas Mortagne 1.19 25
Thomas Mortagne 1.42 26 {{groovy}}
Thomas Mortagne 1.16 27 def hash(str)
28 {
Thomas Mortagne 1.49 29 md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(str)
Thomas Mortagne 1.71 30 println "1. It's then md5 hashed (##${md5}##)."
Thomas Mortagne 1.32 31
Thomas Mortagne 1.49 32 char0 = md5.charAt(0)
Thomas Mortagne 1.71 33 println "1. A first folder is created with the first character of the hash (##${char0}##)."
Thomas Mortagne 1.32 34
Thomas Mortagne 1.49 35 char1 = md5.charAt(1)
Thomas Mortagne 1.71 36 println "1. A second folder is created with the second character of the hash (##${char1}##)."
Thomas Mortagne 1.32 37
Thomas Mortagne 1.49 38 charn = md5.substring(2)
Thomas Mortagne 1.71 39 println "1. A third folder is created with the remaining characters (##${charn}##)."
Thomas Mortagne 1.9 40
Thomas Mortagne 1.52 41 return "**$char0**/**$char1**/**$charn**"
Thomas Mortagne 1.16 42 }
43
Thomas Mortagne 1.17 44 def encodeDocument(documentReference) {
Thomas Mortagne 1.24 45 path = ""
Thomas Mortagne 1.29 46 if (documentReference.root.type == org.xwiki.model.EntityType.WIKI) {
Thomas Mortagne 1.71 47 println "1. A folder with the wiki name (##${documentReference.root.name}##) is created."
Thomas Mortagne 1.29 48 path = documentReference.root.name
Thomas Mortagne 1.24 49 }
50
Thomas Mortagne 1.49 51 localuid = services.component.getInstance(org.xwiki.model.reference.EntityReferenceSerializer.TYPE_STRING, "local/uid").serialize(documentReference)
Thomas Mortagne 1.71 52 println "1. The document reference is serialized using the local uid serializer (##${localuid}##)."
Thomas Mortagne 1.32 53
Thomas Mortagne 1.24 54 path += "/" + hash(localuid)
55
56 return path;
Thomas Mortagne 1.16 57 }
58
Thomas Mortagne 1.17 59 def encodeAttachment(attachmentReference) {
Thomas Mortagne 1.16 60 path = ""
61 if (reference.parent) {
62 path = encodeDocument(attachmentReference.parent)
63 }
64
Thomas Mortagne 1.49 65 path += "/attachments"
Thomas Mortagne 1.71 66 println "1. An ##attachments## folder is created."
Thomas Mortagne 1.16 67
Thomas Mortagne 1.49 68 name = attachmentReference.name
Thomas Mortagne 1.71 69 println "1. The attachment name (##$name##) is extracted from the reference."
Thomas Mortagne 1.49 70
Thomas Mortagne 1.51 71 path += "/" + hash(name) + ""
Thomas Mortagne 1.16 72 }
73
Thomas Mortagne 1.22 74 if (request.serialize_document != null) {
Thomas Mortagne 1.28 75 reference = services.component.getInstance(org.xwiki.model.reference.EntityReferenceResolver.TYPE_STRING, "relative").resolve(request.reference, org.xwiki.model.EntityType.DOCUMENT)
Thomas Mortagne 1.16 76
Thomas Mortagne 1.70 77 println "{{info}}"
78 path = encodeDocument(reference)
79 println "{{/info}}"
80
81 println ""
82 print "The final path of the folder which contains the content of this attachment is ##$path##."
Thomas Mortagne 1.22 83 } else if (request.serialize_attachment != null) {
Thomas Mortagne 1.23 84 reference = services.component.getInstance(org.xwiki.model.reference.EntityReferenceResolver.TYPE_STRING, "relative").resolve(request.reference, org.xwiki.model.EntityType.ATTACHMENT)
Thomas Mortagne 1.16 85
Thomas Mortagne 1.41 86 println "{{info}}"
Thomas Mortagne 1.36 87 path = encodeAttachment(reference)
Thomas Mortagne 1.41 88 println "{{/info}}"
Thomas Mortagne 1.36 89
Thomas Mortagne 1.33 90 println ""
Thomas Mortagne 1.70 91 print "The final path of the folder which contains the content of this document is ##$path##."
Thomas Mortagne 1.2 92 }
93 {{/groovy}}
Thomas Mortagne 1.18 94 {{/box}}
Thomas Mortagne 1.55 95
Michael Hamann 14.1 96 == Attachments and deleted attachments ==
Thomas Mortagne 1.60 97
Thomas Mortagne 1.65 98 By default, inside the attachment folder, you will find both the current version of the attachment and its history. The file name is always ##f##, it's then optionally followed by the version (when it's a piece of the history) and the original file extension.
Thomas Mortagne 1.55 99
Thomas Mortagne 4.1 100 For example for an image of type png:
101
Thomas Mortagne 1.59 102 * ##f.png##
Thomas Mortagne 1.64 103 * ##fv1.1.png##
104 * ##fv2.1.png##
Thomas Mortagne 1.59 105
Thomas Mortagne 1.55 106 {{version since="16.4.0"}}
Thomas Mortagne 7.1 107 Most of the time, instead of the current version of the attachment, a "link" is created. It's a file which name is suffixed with ##.lnk## which contains the relative path it's representing. In standard condition, it always points to the latest version of the attachment.
Thomas Mortagne 1.59 108
Thomas Mortagne 9.1 109 For example for an image of type png:
Thomas Mortagne 4.1 110
Thomas Mortagne 2.1 111 * ##f.png.lnk## {{info}}contains the string ##fv2.1.png##{{/info}}
Thomas Mortagne 1.63 112 * ##fv1.1.png##
113 * ##fv2.1.png##
Thomas Mortagne 5.1 114 {{/version}}
Thomas Mortagne 1.67 115
Thomas Mortagne 11.2 116 The different between attachments and deleted attachment is the location of that folder inside the document folder:
Michael Hamann 14.1 117
Thomas Mortagne 11.2 118 * an attachment is located in ##/attachments/<hash/based/on the attachment name>/##
119 * a deleted is located in ##/deleted-attachments/<hash/based/on the attachment name>/<index of the deleted attachment>/##
Thomas Mortagne 1.67 120
Michael Hamann 14.1 121 == Deleted documents ==
Thomas Mortagne 9.1 122
Thomas Mortagne 13.1 123 {{todo/}}

Get Connected