如何从 Android 发送图像数据到服务器

4
我正在尝试编写一个Android应用程序,它可以拍摄照片,将数据(byte[])与一些元数据放入对象中,并将其发送到AppEngine服务器,在那里它将作为Blob持久化到数据存储中。我不想在Android上将图像保存为文件(除非绝对必要)。我搜索了解决方案,但没有找到清晰或足够具体的内容。我的问题是:
  1. 如何将对象发布到我的servlet?特别是如何正确序列化对象并获取序列化输出以进行HttpPost或类似操作。
  2. 一旦我将blob持久化到数据存储中,我将如何能够检索它作为图像显示在网页上?
代码示例会非常有帮助。此外,如果我采取的方法过于复杂或有问题,请建议其他方法(例如,将图像保存为文件,发布到服务器,然后删除)。

@androidkia:你有没有看过我的答案? - Tofeeq Ahmad
5个回答

8
你只需要进行Http-FileUpload,这是POST请求的特殊情况。 不需要uuencode文件。 不需要使用特殊的lib/jar。 不需要将对象保存到磁盘(无论以下示例是否这样做)。
使用java.net.URLConnection发送和处理HTTP请求下找到关于Http-Command以及"file upload"的非常好的解释。
从那里可以找到文件上传示例(查看"send binary file"),也可以添加一些附属数据。
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 = null;
    try {
        reader = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), charset));
        for (String line; (line = reader.readLine()) != null;) {
            writer.append(line).append(CRLF);
        }
    } finally {
        if (reader != null) 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 = null;
    try {
        input = new FileInputStream(binaryFile);
        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 {
        if (input != null) 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 common files)时,将blob作为图像传递并不是什么大问题。
以下是如何在servlet中接受文件。
public void doPost(HttpServletRequest pRequest, HttpServletResponse pResponse)
        throws ServletException, IOException {
    ServletFileUpload upload = new ServletFileUpload();

    try {
        FileItemIterator iter = upload.getItemIterator (pRequest);

        while (iter.hasNext()) {
            FileItemStream item = iter.next();

            String fieldName = item.getFieldName();

                InputStream stream = item.openStream();
            ....
                stream.close();
            }
    ...

这段代码会展示一张图片

public void doGet (HttpServletRequest pRequest, HttpServletResponse pResponse)  throws IOException {

    try {
        Blob img = (Blob) entity.getProperty(propImg);

        pResponse.addHeader ("Content-Disposition", "attachment; filename=abc.png");
        pResponse.addHeader ("Cache-Control", "max-age=120");

        String enc = "image/png";
        pResponse.setContentType (enc);
        pResponse.setContentLength (img.getBytes().length);
        OutputStream out = pResponse.getOutputStream ();
        out.write (img.getBytes());
        out.close();

我希望这些代码片段能够帮助回答你的问题。

你好,我尝试使用您的答案解决一个非常类似的问题,但对我来说不起作用。您能否请看一下这个链接?http://stackoverflow.com/questions/25309464/send-image-from-android-client-to-appengine-cloud-endpoint - vandus

6

1.您可以使用以下方式将byte[]编码为base64:

http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html

2.使用HTTP POST请求将该数据发送到AppEngine servlet。

3.配置AppEngine以接受servlet。

4.然后,您可以选择将其保存到Datastore或Blobstore。我更喜欢Blobstore这些事情。

5.在服务器端解码base64字符串。

6.从那时起,您需要将字符串切成较小的部分,并将每个部分写入Blobstore。

以下是一些代码,用于将其写入Blobstore。

byte[] finalImageArray = null;

    try {
        finalImageArray = Base64.decode(finalImageData.getBytes()); //finalImageData is the string retrieved from the HTTP POST
    } catch (Base64DecoderException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }


    try{
        FileService fileService = FileServiceFactory.getFileService();

        AppEngineFile file = fileService.createNewBlobFile("image/png");

        FileWriteChannel writeChannel = fileService.openWriteChannel(file, true);

        int steps = (int) Math.floor(finalImageArray.length/1000);
        int current = 0;
        for(int i = 0; i < 1000; i++){
            writeChannel.write(ByteBuffer.wrap(Arrays.copyOfRange(finalImageArray, current, steps+current)));
            current = current + steps;
        }
        writeChannel.write(ByteBuffer.wrap(Arrays.copyOfRange(finalImageArray, current, finalImageArray.length)));  //The reason it's cut up like this is because you can't write the complete string in one go.

        writeChannel.closeFinally();

        blobKey = fileService.getBlobKey(file);

        if(blobKey == null)
            blobKey = retryBloBKey(file); //My own method, AppEngine tends to not return the blobKey once a while.

    }
    catch(Exception e){
        logger.log(Level.SEVERE,e.getMessage());
    }


            return blobKey.getKeyString();
  1. 编写一个servlet,在其中使用提供的键检索图像数据。

  2. 享受您美丽的代码 :)

//我之所以以二进制形式保存它,是因为这样可以让我有更多选项来操作图像api,您也可以选择将其保存在base64格式中。


这种方法看起来很有趣,但我还有几个问题:如何将我的对象转换为byte[]?我应该使用哪个HttpPost命令?是setEntity()吗?如果是,我该如何从序列化的对象创建实体?谢谢。 - mstath
查看相机API,它返回一个byte[],你可以将其转换为base64字符串。而ErikR提供的发送文件的答案似乎是个好主意。 - Rohan
2
除非你真的想浪费空间并使事情变慢,否则没有理由将数据编码为base64。你可以直接将二进制文件(例如图片)上传到Blobstore或者你的应用程序代码中。 - Nick Johnson

2
由于代码由三个人提供,因此您可以选择其中任何一个(或在Google上获得很多示例)。
有三种可能性:
1)首先,您的SD卡中有图像
2)您正在从服务器下载图像,然后发送
3)图像存储在数据库中作为blob
针对每种情况的最佳方法是-
1)如果您的图像存储在sdcard中,则只需获取图像,将其转换为位图->字节数组->字符串并发送到服务器即可。
2)如果您正在从服务器下载图像,然后将其发送到另一个URL。然后下载所有图像URL存储在数据库表中。稍后当您需要发送到服务器时,获取URL,下载图像并存储在缓存中。然后类似地将其发送到服务器并刷新缓存。
3)建议不要在DB中存储大量图像。但是,如果您有必要这样做,请获取Blob将其转换为字节数组并发送到服务器。 Reading blob - 从 SQLite 数据库中读取 BLOB

从 sdcard 读取
List<Integer> drawablesId = new ArrayList<Integer>();
int picIndex=12345;
 File sdDir = new File("/sdcard/pictures");
File[] sdDirFiles = sdDir.listFiles();
for(File singleFile : sdDirFiles)
{
Drawable.createFromPath(singleFile.getAbsolutePath());
/ you can create Bitmap from drawable
 }

希望这能对您有所帮助。


1

不确定这是否符合您的要求,但这将以与浏览器提交表单中图像相同的方式提交图像。您需要下载 httmime-4.1.3.jar 并将其作为引用库添加到您的项目中。

        Bitmap bm;//whatever bitmap you are trying to send
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bm.compress(CompressFormat.JPEG, 75, bos);
        byte[] data = bos.toByteArray();
        HttpClient httpClient = new DefaultHttpClient();
        HttpPost postRequest = new HttpPost("http://yourwebsite.com/whatever");
        ByteArrayBody bab = new ByteArrayBody(data, "image name");
        MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
        reqEntity.addPart("photo", bab);//"photo" is the name of the field that the image is submitted as.
        postRequest.setEntity(reqEntity);
        try {
            HttpResponse response = httpClient.execute(postRequest);
        } catch (ClientProtocolException e) {
            Log.e("asdf", e.getMessage(), e);
        } catch (IOException e) {
            Log.e("asdf", e.getMessage(), e);
        }

1
  1. Android异步Http库提供了一种上传文件到Web服务的方法。我在我的项目中使用它取得了巨大的成功。

  2. 听起来你需要一个接受ID参数并返回文件的Web服务方法。你可以将Blob存储在数据库表中,并以ID为索引进行引用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接