http 400 error uploading file to blobstore with google app engine

70 views
Skip to first unread message

Matt Darwin

unread,
Jun 11, 2014, 2:22:45 PM6/11/14
to google-a...@googlegroups.com

'm trying to upload a file with the google app engine blobstore.

It's supposed to work like this: you call the BlobstoreService to get an upload url, supplying a callback URL. The client is redirected to the upload URL, sends the data, then when they've finished they will be redirected to the callback URL with a couple of parameters which represent the blobstore key.

The dev server behaves differently to the production server as we shall see.

Against production, my code gets as far as completing the upload, then instead of getting a redirect back to my callback URL, I get a 400 error response (implying something wrong with my request). How do I debug this in production? I don't know how to switch on logging for the blobstore.

So I tried to run it locally against the dev server. This time if I don't set the 'content-length' property, I get a 411 (content length not set). But if I try and set that property, I get 'IllegalStateException: Already connected'. Neither of these exceptions happen against production.

So I don't know where to go next. I need to either get it working against dev, in the hope that I can debug the blobstore locally, or work out why it's not working on the blobstore in production.

    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){}

    }
}

NB the serverResponseMessage string above comes back from the production blobstore as "Bad Request"

Vinny P

unread,
Jun 21, 2014, 12:57:22 AM6/21/14
to google-a...@googlegroups.com
On Wed, Jun 11, 2014 at 1:22 PM, Matt Darwin <mattd...@gmail.com> wrote:

Against production, my code gets as far as completing the upload, then instead of getting a redirect back to my callback URL



When you say the code completes the upload, do you see the completed file in the blobstore? How large is the file being uploaded?
 
 
-----------------
-Vinny P
Technology & Media Advisor
Chicago, IL

App Engine Code Samples: http://www.learntogoogleit.com

Reply all
Reply to author
Forward
0 new messages