我正在测试通过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;
}
2个回复我收到了我的POST请求:
413错误(“请求实体太大”)。当我将文件缩小到较小的尺寸时,这会消失。文件大小上传是否有限制?
500错误(“内部服务器错误”)。这是我被困的地方。这可能与datahub.io上的数据集不是“启用数据存储”这一事实有关吗? (我在数据集中的资源文件旁边看到一个禁用的“Data API”按钮,工具提示说: “DataStore被禁用时,此资源无法使用Data API”
=>这500错误的可能原因是什么?如果是这样,我怎么能从客户端启用它? (指向Python代码的指针很有用!)
THX!
PS:我用于测试目的的数据集:http://datahub.io/dataset/testapi
答案 0 :(得分:6)
只有有权访问异常日志的人才能告诉您500正在发生的原因。
但是,我会检查您的请求是否与您从数据存储区一起编写的python客户端获得的请求相同:https://github.com/okfn/ckanclient/blob/master/ckanclient/init.py#L546
您正在多部分请求中发送“bin”图像缓冲区和“comment”file_key。请注意,每次上传都必须更改file_key,因此请添加时间戳或其他内容。也许你需要为二进制文件添加Content-Type:
。
答案 1 :(得分:2)
我一直在经历与这个问题的海报相同的麻烦。经过相当多的试验和错误后,我想出了解决问题的方法。就我而言,我对我想要上传的CKAN存储库有一些控制权。如果你不这样做,你的问题可能无法解决......
我假设您使用的是CKAN的1.8版本?
首先,检查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";