Java http 400使用google app engine将文件上载到blobstore时出错
我正试图用谷歌应用程序引擎blobstore上传一个文件 它应该是这样工作的:您调用BlobstoreService来获取一个上传url,并提供一个回调url。客户端被重定向到上传URL,发送数据,然后当它们完成后,它们将被重定向到回调URL,其中包含两个代表blobstore键的参数 正如我们将看到的,dev服务器的行为与生产服务器不同 在生产环境中,我的代码完成了上传,然后没有重定向回回调URL,而是得到了400个错误响应(这意味着我的请求有问题)。我如何在生产中调试它?我不知道如何打开blobstore的日志记录 因此,我尝试在本地针对dev服务器运行它。这一次如果我没有设置'content length'属性,我会得到411(content length未设置)。但如果我尝试设置该属性,就会得到“IllegalStateException:已连接”。这两种例外情况都不会对生产造成不利影响 所以我不知道下一步该去哪里。我需要让它与dev协同工作,希望我能在本地调试blobstore,或者找出为什么它在生产中不能在blobstore上工作Java http 400使用google app engine将文件上载到blobstore时出错,java,google-app-engine,http,servlets,io,Java,Google App Engine,Http,Servlets,Io,我正试图用谷歌应用程序引擎blobstore上传一个文件 它应该是这样工作的:您调用BlobstoreService来获取一个上传url,并提供一个回调url。客户端被重定向到上传URL,发送数据,然后当它们完成后,它们将被重定向到回调URL,其中包含两个代表blobstore键的参数 正如我们将看到的,dev服务器的行为与生产服务器不同 在生产环境中,我的代码完成了上传,然后没有重定向回回调URL,而是得到了400个错误响应(这意味着我的请求有问题)。我如何在生产中调试它?我不知道如何打开bl
public void upload(String uri, File file) throws IOException {
HttpURLConnection conn=null;
HttpURLConnection conn2=null;
FileInputStream fileInputStream = null;
DataOutputStream dos=null;
try {
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 1 * 1024 * 1024;
// open a URL connection to the Servlet
fileInputStream = new FileInputStream(file);
URL url = new URL(uri);
// Open a HTTP connection to the URL
conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true); // Allow Inputs
conn.setDoOutput(true); // Allow Outputs
conn.setUseCaches(false); // Don't use a Cached Copy
conn.setInstanceFollowRedirects(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("ENCTYPE", "multipart/form-data");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
// conn.setChunkedStreamingMode(1024);
conn.setRequestProperty("content-length", String.valueOf(file.length())); //caused IllegalStateException "Already connected" locally, but not remotely
log("Orignal URL: " + conn.getURL());
//conn.connect(); //TODO duplicates url.openConnection() above?
conn.getInputStream(); //so we can follow the redirect
String redirectedUrl = conn.getHeaderField("Location");
log("Redirected URL: " + redirectedUrl);
//this is horrible and messy but let's get it working then clean it up later
conn.disconnect();
url = new URL(redirectedUrl);
// Open a new HTTP connection to the URL
conn2 = (HttpURLConnection) url.openConnection();
conn2.setDoInput(true); // Allow Inputs
conn2.setDoOutput(true); // Allow Outputs
conn2.setUseCaches(false); // Don't use a Cached Copy
conn2.setInstanceFollowRedirects(false);
conn2.setRequestMethod("POST");
conn2.setRequestProperty("Connection", "Keep-Alive");
conn2.setRequestProperty("ENCTYPE", "multipart/form-data");
conn2.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
conn2.setChunkedStreamingMode(maxBufferSize);
conn2.setRequestProperty("Content-Length", String.valueOf(file.length()));
conn2.connect();
dos = new DataOutputStream(conn2.getOutputStream());
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"myFile\";filename=\""+file.getName()+"\"" + lineEnd);
dos.writeBytes(lineEnd);
// create a buffer of maximum size
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
// send multipart form data necesssary after file data...
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// Responses from the server (code and message)
int serverResponseCode = conn2.getResponseCode();
String serverResponseMessage = conn2.getResponseMessage();
//we are expecting another redirect here
log("aaaargh! 400 instead of 302! "+serverResponseCode+" to "+conn2.getHeaderField("Location"));
}catch (IOException e) {
log(e.getMessage());
throw e;
}catch(Exception e) {
log(e.getMessage());
throw new IOException(e);
} finally {
//close the streams //
if (dos!=null) {
try {
dos.flush();
dos.close();
}catch(IOException ioe){}
}
if (fileInputStream!=null)
try {
fileInputStream.close();
}catch(IOException ioe){}
if (conn!=null )
try {
conn.disconnect();
}catch(Exception ioe){}
if (conn2!=null)
try {
conn2.disconnect();
}catch(Exception ioe){}
}
}
注意:上面的serverResponseMessage字符串从生产blobstore返回为“坏请求”我不知道如何从Android客户端执行此操作,但我可以向您展示我们如何从javascript客户端执行此操作,因为它可能会给您一些提示 客户端非常简单(这是一个angular.js控制器),我们得到了要使用的url并上传到它
// model file upload
$scope.onFileSelect = function ($files) {
$scope.uploadStatus = 'loading';
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
console.log(file);
$http.get('/endpoints/prototype/' + $scope.prototype.key + '/file/url')
.success((function (data) {
console.log(data);
$scope.upload = $upload.upload({
url: data,
withCredentials: true,
file: file
})
.success(function (data) {
console.log(data);
$scope.prototype.modelFile = data;
})
.error(function (data, status) {
console.error(data);
$scope.uploadStatus = 'error';
$scope.uploadError = status;
});
}))
.error((function (data, status) {
console.error(data);
$scope.uploadStatus = 'error';
$scope.uploadError = status;
}));
}
};
最后,上传成功后调用的端点
@RequestMapping(value = "/endpoints/prototype/{key}/file", method = RequestMethod.POST)
@ResponseBody
public String handleFileUpload(@PathVariable("key") String prototypeKey, HttpServletRequest request) {
final Map<String, List<BlobKey>> blobs = blobstoreService.getUploads(request);
final Map<String, List<FileInfo>> allFileInfos = blobstoreService.getFileInfos(request);
final List<BlobKey> blobKeys = blobs.get(INPUT_FILE_NAME);
final List<FileInfo> fileInfos = allFileInfos.get(INPUT_FILE_NAME);
if ((blobKeys == null) || (fileInfos == null)) {
return "You failed to upload a model file for " + prototypeKey + " because the file was empty.";
} else if ((blobKeys.size() == 1) && (fileInfos.size() == 1)) {
final BlobKey blobKey = blobKeys.get(0);
final FileInfo fileInfo = fileInfos.get(0);
final Prototype prototype = prototypeService.findByKey(prototypeKey);
prototype.setModelFile(blobInfoFactory.loadBlobInfo(blobKey).getFilename());
prototype.setUploadedModelFile(fileInfo.getGsObjectName());
prototypeService.updatePrototype(prototypeKey, prototype);
return blobInfoFactory.loadBlobInfo(blobKey).getFilename();
} else {
return "One and only one file is allowed to be uploaded. The related prototype is " + prototypeKey +
". The number of blobs is " + blobKeys.size() + ", the number of files is " + fileInfos.size();
}
}
@RequestMapping(value=“/endpoints/prototype/{key}/file”,method=RequestMethod.POST)
@应答器
公共字符串handleFileUpload(@PathVariable(“key”)字符串原型密钥,HttpServletRequest请求){
final Map blobs=blobstoreService.getUploads(请求);
最终映射allFileInfos=blobstoreService.getFileInfos(请求);
最终列表blobKeys=blobs.get(输入文件名);
最终列表fileInfos=allFileInfos.get(输入文件名);
if((blobKeys==null)| |(fileInfos==null)){
return“您未能上载“+prototypeKey+”的模型文件,因为该文件为空。”;
}else if((blobKeys.size()=1)和&(fileInfos.size()=1)){
final BlobKey-BlobKey=BlobKey.get(0);
final FileInfo FileInfo=fileInfos.get(0);
最终原型=prototypeService.findByKey(prototypeKey);
prototype.setModelFile(blobInfoFactory.loadBlobInfo(blobKey.getFilename());
setUploadedModelFile(fileInfo.getGsObjectName());
prototypeService.updatePrototype(prototypeKey,prototype);
返回blobInfoFactory.loadBlobInfo(blobKey.getFilename();
}否则{
return“只允许上传一个文件,相关原型为”+prototypeKey+
。Blob的数量为“+blobKeys.size()+”,文件的数量为“+fileInfos.size()”;
}
}
就是这样,希望它能帮助您解决您的问题。我通过使用更高级别的API而不是我尝试使用的低级HttpConnection代码来解决这个问题
public String uploadFile(String url, File f) throws Exception {
HttpClient httpClient = new DefaultHttpClient();
String str = "";
HttpPost first = new HttpPost(url); //a GET should work but I was getting 405
HttpResponse firstResponse = httpClient.execute(first);
str = firstResponse.getFirstHeader("Location").getValue();
//Post image to generated url
HttpPost second = new HttpPost(str);
FileBody fileBody = new FileBody(f);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("file", fileBody);
second.setEntity(reqEntity);
httpClient = new DefaultHttpClient();
HttpResponse secondResponse = httpClient.execute(second);
str = secondResponse.getFirstHeader("Location").getValue();
log(str);
return str;
}
此方法返回上载图像的服务URL。正如您所说:您向客户端提供一个上载URL,然后接收一个简单的回调。这段代码是干什么用的?我正在尝试使用google app engine blobstore从android客户端上载一个文件。我怀疑您实际上没有将确切的名称-值对参数处理到后端servlet或控制器。因此你得到了这个问题。还要检查您的文件是否上载到blobstore。
public String uploadFile(String url, File f) throws Exception {
HttpClient httpClient = new DefaultHttpClient();
String str = "";
HttpPost first = new HttpPost(url); //a GET should work but I was getting 405
HttpResponse firstResponse = httpClient.execute(first);
str = firstResponse.getFirstHeader("Location").getValue();
//Post image to generated url
HttpPost second = new HttpPost(str);
FileBody fileBody = new FileBody(f);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("file", fileBody);
second.setEntity(reqEntity);
httpClient = new DefaultHttpClient();
HttpResponse secondResponse = httpClient.execute(second);
str = secondResponse.getFirstHeader("Location").getValue();
log(str);
return str;
}