5

前言 我正在研究一个用java编写的独立于平台的媒体数据库,其中媒体文件由文件哈希标识。用户应该能够移动文件,所以我不想依赖任何文件路径。导入后,我将路径和哈希存储在我的数据库中。我基于准确性和性能之间的权衡开发了一种快速文件哈希 id 算法,但快速并不总是足够快。:)

为了更新和导入媒体文件,我需要(重新)创建库中所有文件的文件哈希。我现在的想法是只计算一次哈希并将其存储在文件元数据(扩展属性)中,以提高支持扩展文件属性的文件系统的性能。(NTFS、HFS+、ext3...)我已经实现了,你可以在这里找到当前源:archimedesJ.io.metadata

尝试 乍一看,Java 1.7 通过UserDefinedFileAttributeView提供了一种处理元数据的好方法。对于大多数平台,这都有效。遗憾的是,UserDefinedFileAttributeView不适用于 HFS+。虽然,我不明白为什么不支持特别是 HFS+ 文件系统 - 它是元数据的主要格式之一?(请参阅相关问题-它不提供任何解决方案

如何使用 Java 在 OS X 上存储扩展文件属性? 为了克服这个 java 限制,我决定使用 OSX 上的xattr命令行工具,并将它与 Java 进程处理一起使用来读取它的输出。我的实现有效,但速度很慢。(重新计算文件哈希更快,多么讽刺!我在 Mac BookPro Retina 上进行测试,带有 SSD。

事实证明,xattr 工具的运行速度很慢。(写起来很慢,但更重要的是读取属性也很慢)为了证明这不是 Java 问题,而是工具本身,我创建了一个简单的 bash 脚本,在几个具有我的自定义属性的文件上使用 xattr 工具:

FILES=/Users/IsNull/Pictures/
for f in $FILES
do
  xattr -p vidada.hash $f
done

如果我运行它,这些行会彼此“快速”出现,但我希望在几毫秒内立即向我显示输出。一点延迟清晰可见,因此我猜该工具不是那么快。在 java 中使用它给了我创建进程、解析输出的额外开销,这使得它变得更慢了。

有没有更好的方法来使用 Java 访问 HFS+ 上的扩展属性?使用 Java 在 OS X 上使用扩展属性的快速方法是什么?

4

3 回答 3

4

OS X/usr/bin/xattr可能相当慢,因为它是作为 Python 脚本实现的。用于设置扩展属性的 C API 是setxattr(2)。这是一个例子:

if(setxattr("/path/to/file",
            attribute_name,
            (void *)attribute_data,
            attribute_size,
            0,
            XATTR_NOFOLLOW) != 0)
{
  /* an error occurred, see errno */
}

您可以创建一个 JNI 包装器以从 Java 访问此函数;您可能还需要 getxattr(2)、listxattr(2) 和 removexattr(2),具体取决于您的应用程序还需要做什么。

于 2013-05-02T17:42:42.457 回答
4

我创建了一个 JNI 包装器,用于现在直接通过 C-API 访问扩展属性。它是一个开源 Java Maven 项目,可在GitHub/xattrj 上获得

作为参考,我在这里发布了有趣的源代码。最新资源请参考以上项目页面。

Xattrj.java

public class Xattrj {

    /**
     * Write the extended attribute to the given file
     * @param file
     * @param attrKey
     * @param attrValue
     */
    public void writeAttribute(File file, String attrKey, String attrValue){
        writeAttribute(file.getAbsolutePath(), attrKey, attrValue);
    }

    /**
     * Read the extended attribute from the given file
     * @param file
     * @param attrKey
     * @return
     */
    public String readAttribute(File file, String attrKey){
        return readAttribute(file.getAbsolutePath(), attrKey);
    }

    /**
     * Write the extended attribute to the given file
     * @param file
     * @param attrKey
     * @param attrValue
     */
    private native void writeAttribute(String file, String attrKey, String attrValue);

    /**
     * Read the extended attribute from the given file
     * @param file
     * @param attrKey
     * @return
     */
    private native String readAttribute(String file, String attrKey);


    static {
        try {
            System.out.println("loading xattrj...");
            LibraryLoader.loadLibrary("xattrj");
            System.out.println("loaded!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

org_securityvision_xattrj_Xattrj.cpp

#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "org_securityvision_xattrj_Xattrj.h"
#include <sys/xattr.h>


/**
 * writeAttribute
 * writes the extended attribute
 *
 */
JNIEXPORT void JNICALL Java_org_securityvision_xattrj_Xattrj_writeAttribute
    (JNIEnv *env, jobject jobj, jstring jfilePath, jstring jattrName, jstring jattrValue){

    const char *filePath= env->GetStringUTFChars(jfilePath, 0);
    const char *attrName= env->GetStringUTFChars(jattrName, 0);
    const char *attrValue=env->GetStringUTFChars(jattrValue,0);

    int res = setxattr(filePath,
                attrName,
                (void *)attrValue,
                strlen(attrValue), 0,  0); //XATTR_NOFOLLOW != 0
    if(res){
      // an error occurred, see errno
        printf("native:writeAttribute: error on write...");
        perror("");
    }
}


/**
 * readAttribute
 * Reads the extended attribute as string
 *
 * If the attribute does not exist (or any other error occurs)
 * a null string is returned.
 *
 *
 */
JNIEXPORT jstring JNICALL Java_org_securityvision_xattrj_Xattrj_readAttribute
    (JNIEnv *env, jobject jobj, jstring jfilePath, jstring jattrName){

    jstring jvalue = NULL;

    const char *filePath= env->GetStringUTFChars(jfilePath, 0);
    const char *attrName= env->GetStringUTFChars(jattrName, 0);

    // get size of needed buffer
    int bufferLength = getxattr(filePath, attrName, NULL, 0, 0, 0);

    if(bufferLength > 0){
        // make a buffer of sufficient length
        char *buffer = (char*)malloc(bufferLength);

        // now actually get the attribute string
        int s = getxattr(filePath, attrName, buffer, bufferLength, 0, 0);

        if(s > 0){
            // convert the buffer to a null terminated string
            char *value = (char*)malloc(s+1);
            *(char*)value = 0;
            strncat(value, buffer, s);
            free(buffer);

            // convert the c-String to a java string
            jvalue = env->NewStringUTF(value);
        }
    }
    return jvalue;
}

现在让我很困扰的makefile让事情正常工作:

CC=gcc
LDFLAGS= -fPIC -bundle
CFLAGS= -c -shared -I/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers -m64


SOURCES_DIR=src/main/c++
OBJECTS_DIR=target/c++
EXECUTABLE=target/classes/libxattrj.dylib

SOURCES=$(shell find '$(SOURCES_DIR)' -type f -name '*.cpp')
OBJECTS=$(SOURCES:$(SOURCES_DIR)/%.cpp=$(OBJECTS_DIR)/%.o)

all: $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(LDFLAGS) $(OBJECTS) -o $@

$(OBJECTS): $(SOURCES)
    mkdir -p $(OBJECTS_DIR)
    $(CC) $(CFLAGS) $< -o $@



clean:
    rm -rf $(OBJECTS_DIR) $(EXECUTABLE)
于 2013-05-03T15:57:39.887 回答
2

我刚刚将我的<sys/xattr.h>JNA 包装器贡献给了 JNA 平台项目。由于它是 JNA,因此您不需要编译任何本机库。:)

@见https://github.com/twall/jna/pull/338

应该是下一个版本 JNA 版本的一部分。

于 2014-06-18T13:17:36.560 回答