2013-05-02 84 views
3

前言 我的工作中,依靠媒体数据库写在媒体文件通过文件哈希标识的Java平台上。用户应该能够移动文件,所以我不想依赖任何文件路径。一旦导入,我将路径和散列存储在我的数据库中。 我开发了一个基于精度和性能之间折衷的fast file-hash-id algorithm,但快速并不总是足够快。 :)如何使用Java在OS X的扩展文件属性中存储散列?

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

尝试 乍一看,Java的1.7报价与UserDefinedFileAttributeView一个很好的方式来处理元数据。对于大多数平台而言,可悲的是,UserDefinedFileAttributeView无法在HFS +上运行。尽管如此,我不明白为什么特别是HFS +文件系统不被支持 - 它是元数据的主要格式之一? (见相关Question - 不提供任何解决方案

如何存储在OS X扩展文件属性与Java? 为了达到这个java限制,我决定使用OSX上的命令行工具,并使用它与Javas Process处理来读取它的输出。 My implementation的作品,但它非常缓慢。 (重新计算文件哈希速度更快,多么具有讽刺意味!我正在使用SSD在Mac BookPro Retina上进行测试

事实证明,xattr工具的工作非常缓慢。 (写作是该死的慢,但更重要的还读取属性是慢) 证明它不是Java的问题,但工具本身,我创建了一个简单的bash脚本上有我的自定义多个文件使用xattr工具属性:

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

如果我运行它,线条出现后相互“快”,但我希望马上告诉我在几毫秒内输出。有一点延迟是清晰可见的,因此我猜这个工具不是那么快。在java中使用它会给我一个创建进程的额外开销,解析输出使其更慢。

有没有更好的方式来访问HFS +与Java扩展属性?使用Java在OS X上使用扩展属性的快速方法是什么?因为它是作为一个Python脚本实现

回答

2

我已经创建了一个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 [email protected] 

$(OBJECTS): $(SOURCES) 
    mkdir -p $(OBJECTS_DIR) 
    $(CC) $(CFLAGS) $< -o [email protected] 



clean: 
    rm -rf $(OBJECTS_DIR) $(EXECUTABLE) 
+0

不错,很高兴看到你工作。只是对你的代码的一些评论......你为什么在第二次调用getxattr()时硬编码255作为缓冲区大小?您已经动态确定了属性值的大小。其次,如果这是为了设置和检索任意属性(而不是特定于应用程序),你不能把所有XATTR值作为字符串。他们可以并经常包含任意的二进制数据;例如,使用Safari下载文件并检查其xattrs。 – jatoben 2013-05-03 17:26:57

+0

我打算创建seaprate方法来支持二进制数据。缓冲区大小来自官方的OS X例子,我只是用同样的方法。我会修改你的建议。 BTW:我在C/C++的经验是相当有限的,因此任何其他的建议高度赞赏:)比如,我想因为我使用C++ string.h中我的C-字符串的处理可以简化... – IsNull 2013-05-03 17:43:59

4

OS X的/usr/bin/xattr可能相当缓慢。用于设置扩展属性的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),具体取决于您的应用需要做什么。

+0

我会给JNI接近尝试发布我的结果! – IsNull 2013-05-02 19:01:18

+0

我创建JNI封装(见我的回答)和它的现在速度极快。谢谢! – IsNull 2013-05-03 16:00:06

相关问题