8

我正在测试通过 API 的 Java 客户端将文件上传到 CKAN/datahub.io 上的数据集。

public String uploadFile()
        throws CKANException {

    String returned_json = this._connection.MultiPartPost("", "");

    System.out.println("r: " + returned_json);
    return returned_json;
}

   protected String MultiPartPost(String path, String data)
            throws CKANException {
        URL url = null;

        try {
            url = new URL(this.m_host + ":" + this.m_port + path);
        } catch (MalformedURLException mue) {
            System.err.println(mue);
            return null;
        }

        String body = "";

        HttpClient httpclient = new DefaultHttpClient();
        try {
            String fileName = "D:\\test.jpg";

            FileBody bin = new FileBody(new File(fileName),"image/jpeg");
            StringBody comment = new StringBody("Filename: " + fileName);

            MultipartEntity reqEntity = new MultipartEntity();
            reqEntity.addPart("bin", bin);
            reqEntity.addPart("comment", comment);
            HttpPost postRequest = new HttpPost("http://datahub.io/api/storage/auth/form/2013-01-24T130158/test.jpg");
            postRequest.setEntity(reqEntity);
            postRequest.setHeader("X-CKAN-API-Key", this._apikey);
            HttpResponse response = httpclient.execute(postRequest);
            int statusCode = response.getStatusLine().getStatusCode();
            System.out.println("status code: " + statusCode);

            BufferedReader br = new BufferedReader(
                    new InputStreamReader((response.getEntity().getContent())));

            String line;
            while ((line = br.readLine()) != null) {
                body += line;
            }
            System.out.println("body: " + body);
        } catch (IOException ioe) {
            System.out.println(ioe);
        } finally {
            httpclient.getConnectionManager().shutdown();
        }

        return body;
    }

我收到 POST 请求的 2 个响应:

  • 当我尝试上传的 jpeg 为 2.83 Mb 时出现 413 错误(“请求实体太大”)。当我将文件缩小到更小的尺寸时,这会消失。文件大小上传有限制吗?

  • 500 错误(“内部服务器错误”)。这就是我卡住的地方。这可能与我在 datahub.io 上的数据集未“启用数据存储”这一事实有关?(我在数据集中的资源文件旁边看到一个禁用的“数据 API”按钮,工具提示说:“由于数据存储已禁用,此资源的数据 API 不可用”

=> 是这个 500 错误的可能原因吗?如果是这样,我如何从客户端启用它?(指向 Python 代码的指针会很有用!)

谢谢!
PS:我用于测试目的的数据集:http: //datahub.io/dataset/testapi

4

2 回答 2

6

只有有权访问异常日志的人才能告诉您为什么会发生 500。

但是,我会检查您的请求是否与您从与数据存储一起编写的 python 客户端获得的请求相同:https : //github.com/okfn/ckanclient/blob/master/ckanclient/init .py# L546

您在多部分请求中发送“bin”图像缓冲区和“comment”file_key。请注意,每次上传都必须更改 file_key,因此请添加时间戳或其他内容。也许您需要Content-Type:为二进制文件添加一个。

于 2013-01-31T16:59:38.457 回答
2

我一直在经历与这个问题的海报相同的麻烦。经过相当多的反复试验,我想出了一个解决问题的办法。就我而言,我对想要上传到的 CKAN 存储库有一些控制权。如果你不这样做,你的问题可能无法解决......

我假设您使用的是 1.8 版本的 CKAN?

首先,检查 CKAN 存储库是否已设置为允许文件上传,如果没有,请将其配置为允许。这可以使用此处发布的步骤在服务器上完成:http: //docs.ckan.org/en/ckan-1.8/filestore.html#local-file-storage

接下来应该解决您提到的 413 错误。这与服务器的一般配置有关。就我而言,CKAN 是通过 nginx 托管的。我在 nginx.conf 文件中添加了“client_max_body_size 100M”行。例如看这篇文章:http ://recursive-design.com/blog/2009/11/18/nginx-error-413-request-entity-too-large/

然后就只剩下500错误了。在撰写本文时,CKAN 的 api 文档还有些不成熟......它确实说你必须像你为文件上传所做的那样构建一个请求。但是,此请求只是请求文件上传的权限。如果您的凭据检查文件上传(并非每个用户都可能被允许上传文件),响应包含一个对象,告诉您将文件发送到哪里......由于不明确的 api,您最终合并了这两个请求。

以下场景显示了处理文件上传的两个请求的后续处理。在您的情况下,场景中的某些步骤可能会有所不同,因为存储库的设置略有不同。如果您收到错误消息,请务必检查响应的正文以获取线索!

这是我使用的身份验证请求:

String body = "";
String generatedFilename=null;

HttpClient httpclient = new DefaultHttpClient();

try {

    // create new identifier for every file, use time
    SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyyMMMddHHmmss");
    dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
    String date=dateFormatGmt.format(new Date());
    generatedFilename=date +"/"+filename;

    HttpGet getRequest = new HttpGet(this.CKANrepos+ "/api/storage/auth/form/"+generatedFilename);
    getRequest.setHeader(CKANapiHeader, this.CKANapi);

    HttpResponse response = httpclient.execute(getRequest);
    int statusCode = response.getStatusLine().getStatusCode();
    BufferedReader br = new BufferedReader(
             new InputStreamReader((response.getEntity().getContent())));

    String line;
    while ((line = br.readLine()) != null) {
         body += line;
    }
    if(statusCode!=200){
         throw new IllegalStateException("File reservation failed, server responded with code: "+statusCode+
          "\n\nThe message was: "+body);

    }
}finally {
     httpclient.getConnectionManager().shutdown();
}

现在,如果一切顺利,服务器会响应一个 json 对象,其中包含在进行实际文件上传时要使用的参数。就我而言,该对象看起来像:

{file_key:"some-filename-to-use-when-uploading"}

不过请务必检查 json 对象,因为我了解到可能存在需要更多或不同参数的自定义 ckan 存储库。

然后可以在实际的文件上传中使用这些响应:

        File file = new File("/tmp/file.rdf");
        String body = "";

        HttpClient httpclient = new DefaultHttpClient();

        try {

            FileBody bin = new FileBody(file,"application/rdf+xml");

            MultipartEntity reqEntity = new MultipartEntity();
            reqEntity.addPart("file", bin);

            reqEntity.addPart("key", new StringBody(filename));


            HttpPost postRequest = new HttpPost(this.CKANrepos+"/storage/upload_handle");
            postRequest.setEntity(reqEntity);
            postRequest.setHeader(CKANapiHeader, this.CKANapi);
            HttpResponse response = httpclient.execute(postRequest);
            int statusCode = response.getStatusLine().getStatusCode();
            BufferedReader br = new BufferedReader(
                    new InputStreamReader((response.getEntity().getContent())));

            String line;
            while ((line = br.readLine()) != null) {
                body += line;
            }
            if(statusCode!=200){
                getWindow().showNotification("Upload Statuscode: "+statusCode,
                        body,
                        Window.Notification.TYPE_ERROR_MESSAGE);

            }
        }finally {
            httpclient.getConnectionManager().shutdown();
        }

如您所见,file_key 属性现在已转换为简单的“key”属性。我不知道为什么。

这将使您的文件上传。对此上传请求的响应将包含一个 json 对象,告诉您文件上传到哪里。编辑:实际上似乎我的ckan用一个简单的html页面响应告诉我文件已上传...我必须解析页面以确认文件已正确上传:(

就我而言,该文件位于

this.CKANrepos +"/storage/f/"+location

其中 location 是身份验证阶段返回的文件名。

在前面的代码片段中:

//the location of your ckan repository, including /api and possibly version, e.g.
this.CKANrepos = "http://datahub.io/api/3/";
this.CKANapiHeader="X-CKAN-API-Key";
this.CKANapi = "your ckan api key here";
于 2013-02-24T19:47:40.870 回答