2013-05-02 72 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

相關問題