Locate an Attachment's File on Disk

Last modified by Eleni Cojocariu on 2026/10/23 14:40

Steps

Find the file holding an attachment's content under store/file, whose directories are named after hashes rather than after the attachment, as Filesystem Store Layout describes.

The script below does the hashing for you. Running Groovy in a Page needs programming right, which is why it is given here rather than run on this Page.

  1. Note the Page the attachment is on and its file name, for instance Sandbox.WebHome and XWikiLogo.png.
  2. Create a Page on your own wiki and paste the script into it, in the wiki editor:
    {{groovy}}
    import org.apache.commons.codec.digest.DigestUtils
    import org.xwiki.model.reference.DocumentReference
    import org.xwiki.model.reference.EntityReferenceSerializer
    
    // The attachment to locate, as "Space.Page@filename".
    def target = "[email protected]"
    
    def (page, filename) = target.split("@", 2)
    // Resolve, then drop the locale: the store hashes the Page without it.
    def docRef = new DocumentReference(services.model.resolveDocument(page), (Locale) null)
    def localUid = services.component
        .getInstance(EntityReferenceSerializer.TYPE_STRING, "local/uid")
        .serialize(docRef)
    
    def split = { s -> def h = DigestUtils.md5Hex(s); "${h.charAt(0)}/${h.charAt(1)}/${h.substring(2)}" }
    def path = "${docRef.wikiReference.name}/${split(localUid)}/attachments/${split(filename)}"
    
    println "|=Step|=Value"
    println "|The Page, serialized with ##local/uid##|##${localUid}##"
    println "|Its MD5|##${DigestUtils.md5Hex(localUid)}##"
    println "|The MD5 of ##${filename}##|##${DigestUtils.md5Hex(filename)}##"
    println "|The directory, under ##store/file##|##${path}##"
    {{/groovy}}
  3. Change the target line to the Page and file name you noted, separated by @, then save the Page.
  4. Read the directory off the last row of the table and look for it under store/file in the permanent directory. The file inside it is called f plus the extension of the attachment.

    The script's output table, its last row giving the directory under store/file

FAQ

The directory is not there. What went wrong?

Either the attachment is on a different Page than you think, or its content is not in the filesystem store: a wiki switched to the database store keeps the files it had already written on disk and puts every later one in the database.

Can I run this against a deleted attachment?

Not as written. A deleted attachment keeps the same two hashes but sits under deleted-attachments with an extra index level, so take the two hashes the script prints and read Filesystem Store Layout for where they go.

Is it safe to read the file while XWiki is running?

Reading it is. Writing to it, renaming it or deleting it is not: XWiki recomputes the path on every access and keeps the attachment's metadata in the database, so a file changed behind its back is at best ignored and at worst an attachment that can no longer be downloaded.

Related

Get Connected