Движок приложения Google: исключение NullpointerException при сохранении изображения в Blobstore через сервлет Java - PullRequest
1 голос
/ 17 мая 2011

Я пытаюсь загрузить изображения с помощью файла JSP и сервлета проверки в Blobstore. часть JSP выглядит следующим образом:

<form action="/testuploadmimevalidation?provider-key=testprovider" method="post" enctype="multipart/form-data">
    <input type="text" name="foo">
    <input type="file" name="myfile" >
    <input type="submit" value="Submit">
</form>

Класс Java TestUploadMimeValidation выглядит следующим образом:

public class TestUploadMimeValidation extends HttpServlet {

    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
    private static final Logger log = Logger.getLogger(TestUploadMimeValidation.class.getName());
    private static final boolean PRODUCTION_MODE = SystemProperty.environment.value() == SystemProperty.Environment.Value.Production;
    private static final String URL_PREFIX = PRODUCTION_MODE ? "" : "http://127.0.0.1:8080";

    public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        InputStream in = req.getInputStream();
        int formDataLength = req.getContentLength();
        byte dataBytes[] = new byte[formDataLength];
        int len;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        while ((len = in.read(dataBytes, 0, formDataLength)) != -1)
            bos.write(dataBytes, 0, len);

        dataBytes = bos.toByteArray();
        String urlStr = URL_PREFIX + BlobstoreServiceFactory.getBlobstoreService().createUploadUrl("/testupload");
        URLFetchService urlFetch = URLFetchServiceFactory.getURLFetchService();
        HTTPRequest request = new HTTPRequest(new URL(urlStr), HTTPMethod.POST, FetchOptions.Builder.withDeadline(20.0));
        request.setHeader(new HTTPHeader("Content-Type", "multipart/form-data"));
        request.setPayload(dataBytes);

        System.out.println("step1");

        try {
            HTTPResponse response = urlFetch.fetch(request);
            System.out.println("step2");
        } catch (IOException e) {

        } catch (NullPointerException e) {

        }

        System.out.println("step3");
    }
}

Этот класс отлично загружает изображение в Blobstore, но я получаю NullPointerException. трассировка стека выглядит следующим образом:

WARNING: /_ah/upload/ag5tOGJ5dXMtZGV2ZWxvcHIbCxIVX19CbG9iVXBsb2FkU2Vzc2lvbl9fGAIM
java.lang.NullPointerException
        at javax.mail.internet.MimeMultipart.writeTo(MimeMultipart.java:143)
        at com.google.appengine.api.blobstore.dev.UploadBlobServlet.handleUpload(UploadBlobServlet.java:180)
        at com.google.appengine.api.blobstore.dev.UploadBlobServlet.access$000(UploadBlobServlet.java:72)
        at com.google.appengine.api.blobstore.dev.UploadBlobServlet$1.run(UploadBlobServlet.java:101)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.google.appengine.api.blobstore.dev.UploadBlobServlet.doPost(UploadBlobServlet.java:98)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
        at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:58)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
        at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
        at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:122)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
        at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
        at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
        at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
        at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
        at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
        at com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:70)
        at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
        at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:351)
        at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
        at org.mortbay.jetty.Server.handle(Server.java:326)
        at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
        at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:938)
        at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:755)
        at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
        at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
        at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
        at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

Из-за этого NPE мой код в классе TestUpload.java не выполняется. println получилось хорошо. Это печатает "step3". Изображение сохраняется в хранилище данных. Но я просто не могу заставить код в TestUpload работать. У вас есть идеи, что может быть причиной исключения? Я пытался связываться с составной частью, но не очень успешно.

Любая помощь по этому вопросу будет очень признательна.

Ответы [ 3 ]

4 голосов
/ 17 мая 2011

Я использую

<form action="<%= blobstoreService.createUploadUrl("/catalog/actions/add") %>" method="post" enctype="multipart/form-data" >
1 голос
/ 13 октября 2013

Сначала вы можете увидеть, как выполнить запрос POST здесь: Использование java.net.URLConnection для запуска и обработки HTTP-запросов

String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just
// generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type",
    "multipart/form-data; boundary=" + boundary);
PrintWriter writer = null;
try {
    OutputStream output = connection.getOutputStream();
    writer = new PrintWriter(new OutputStreamWriter(output, charset),
            true); // true = autoFlush, important!
    // Send normal param.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"param\"")
            .append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF);
    writer.append(param).append(CRLF).flush();
    // Send text file.
    writer.append("--" + boundary).append(CRLF);
    writer.append(
        "Content-Disposition: form-data; name=\"textFile\"; filename=\""
            + textFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF).flush();
    BufferedReader reader = new BufferedReader(new InputStreamReader(
            new FileInputStream(textFile), charset));
    try {
        for (String line; (line = reader.readLine()) != null;) {
            writer.append(line).append(CRLF);
        }
    } finally {
        try { reader.close(); } catch (IOException logOrIgnore) {}
    }
    writer.flush();
    // Send binary file.
    writer.append("--" + boundary).append(CRLF);
    writer.append(
        "Content-Disposition: form-data; name=\"binaryFile\"; filename=\""
            + binaryFile.getName() + "\"").append(CRLF);
    writer.append(
        "Content-Type: "
            + URLConnection.guessContentTypeFromName(binaryFile
                .getName())).append(CRLF);
    writer.append("Content-Transfer-Encoding: binary").append(CRLF);
    writer.append(CRLF).flush();
    InputStream input = new FileInputStream(binaryFile);
    try {
        byte[] buffer = new byte[1024];
        for (int length = 0; (length = input.read(buffer)) > 0;) {
            output.write(buffer, 0, length);
        }
        output.flush(); // Important! Output cannot be closed. Close of
                        // writer will close output as well.
    } finally {
        try { input.close(); } catch (IOException logOrIgnore) {}
    }
    writer.append(CRLF).flush(); // CRLF is important! It indicates end
                                    // of binary boundary.
    // End of multipart/form-data.
    writer.append("--" + boundary + "--").append(CRLF);
} finally {
    if (writer != null) writer.close();
}

Я пытался с библиотекой Apache http-client, и она работала, но только локально. Из-за ограничения сокета мы не можем его использовать. https://developers.google.com/appengine/docs/java/sockets/#limitations-and-restrictions Мы застряли, чтобы использовать: java.net.URL; java.net.URLConnection;

Для изображений используйте двоичный файл по ссылке выше. Это была боль в заднице, пока я не получил рабочий код:)

0 голосов
/ 24 ноября 2015

TL; DR: для тех, кто получил эту специфически странную ошибку при попытке использовать AJAX для загрузки вместо традиционного маршрута отправки формы, решением является , чтобы НЕ устанавливать заголовок «Content-Type» при создании AJAXзапрос .После удаления этого он начал работать для меня.

Подробнее: По-видимому, передача объекта FormData в качестве тела для запроса AJAX автоматически установит заголовок «Content-Type» с «multipart / form-data»", а также требуемую граничную строку (что будет отсутствовать, если мы сами установим тип содержимого).

Дополнительная информация: Эта граничная строка, AFAIK, генерируется случайным образом, и мы не можем определить нашусобственную границу через заголовок «Content-Type» (я пробовал, и граница, используемая в теле, все еще является случайно сгенерированной строкой, вызывая ошибку пространства кучи).Запрос AJAX должен назначать случайно сгенерированную граничную строку в заголовке, а установка нашего собственного типа содержимого перезаписывает этот заголовок.

Я получил подсказку: html form enctype на ajax (хотя jQuery имеет отношение, логика установки заголовка Content-type является родной логикой webkit / browser)

...