如何通过 HttpUrlConnection 将图像从 Android 客户端发送到 Node.js 服务器?

How to send an image from Android client to Node.js server via HttpUrlConnection?

我正在尝试使用 HttpUrlConnection 将图像发送到服务器,因为它是 Google 推荐的。我决定将图像转换为 Base64 字符串并将其发送到服务器,然后我将其解码为 .jpg 文件。但是这种方法只适用于小尺寸的缩略图,我不能发送全尺寸的图像。

这里是 android 客户端代码:

 public static void postData(Bitmap imageToSend) {



        try

        {
            URL url = new URL("http://");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");

            conn.setDoInput(true);
            conn.setDoOutput(true);

            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Cache-Control", "no-cache");

            conn.setReadTimeout(35000);
            conn.setConnectTimeout(35000);


            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            // writes a compress version of Bitmap to a specified outputstream
            imageToSend.compress(Bitmap.CompressFormat.JPEG, 100, bos);

            byte[] byteArray = bos.toByteArray();
            String imageEncoded = Base64.encodeToString(byteArray, Base64.DEFAULT);

            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("image", imageEncoded));


            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));

            // getQuery is function for creating a URL encoded string
            writer.write(getQuery(params));

            System.out.println("Response Code: " + conn.getResponseCode());

            InputStream in = new BufferedInputStream(conn.getInputStream());
            Log.d("sdfs", "sfsd");
            BufferedReader responseStreamReader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            StringBuilder stringBuilder = new StringBuilder();
            while ((line = responseStreamReader.readLine()) != null)
                stringBuilder.append(line).append("\n");
            responseStreamReader.close();

            String response = stringBuilder.toString();
            System.out.println(response);

            bos.flush();
            bos.close();
            in.close();
            conn.disconnect();

        }

        catch(MalformedURLException e) {
            e.printStackTrace();
        }

        catch(IOException e) {
            e.printStackTrace();
        }


}

Node.js 服务器代码:

function base64_decode(base64str,file) {
   var bitmap = new Buffer(base64str,'base64');
   //writing into an image file
   fs.writeFile(file, bitmap);
   //write a text file
    console.log('File created from base64 encoded string');
}

app.post("/", function (req,res,next) {
        //requesting the value of the image key which is urlencoded base 64 string
        var image = req.body.image;
        console.log(req.body.image);
        base64_decode(image,'newImage.jpg');

        res.writeHead(200, {'Content-Type':'text/plain'});
        res.write('the image is saved');
        res.end();
        if (req.url != "/")
           next();
 

我不能对全尺寸图像使用相同的方法,因为 BufferedWriter 大小限制 - base64 编码的字符串对它来说太长了。

另一种方法是使用HttpPost和MultipartEntity,但是在API22中都被弃用了,我不知道如何在服务器端处理请求。在其他示例中,使用了一些包装器,如两个连字符、边界、crlf,但我找不到原因。

我需要一个 HttpUrlConnection 的例子

感谢任何帮助,因为我是 Android 和 node.js

的新手

我建议上传二进制数据。您可以将图像元数据(如名称、类型、用户 ID 等)作为 url 参数或自定义 http-headers (X-...)。

Android客户端代码(未测试!):

 public static void postData(Bitmap imageToSend) {
        try
        {
            URL url = new URL("http://myserver/myapp/upload-image");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");

            conn.setDoInput(true);
            conn.setDoOutput(true);

            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Cache-Control", "no-cache");

            conn.setReadTimeout(35000);
            conn.setConnectTimeout(35000);

            // directly let .compress write binary image data
            // to the output-stream
            OutputStream os = conn.getOutputStream();
            imageToSend.compress(Bitmap.CompressFormat.JPEG, 100, os);
            os.flush();
            os.close();

            System.out.println("Response Code: " + conn.getResponseCode());

            InputStream in = new BufferedInputStream(conn.getInputStream());
            Log.d("sdfs", "sfsd");
            BufferedReader responseStreamReader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            StringBuilder stringBuilder = new StringBuilder();
            while ((line = responseStreamReader.readLine()) != null)
                stringBuilder.append(line).append("\n");
            responseStreamReader.close();

            String response = stringBuilder.toString();
            System.out.println(response);

            conn.disconnect();
        }
        catch(MalformedURLException e) {
            e.printStackTrace();
        }
        catch(IOException e) {
            e.printStackTrace();
        }
}

Node.js,快递编码:

function rawBody(req, res, next) {
    var chunks = [];

    req.on('data', function(chunk) {
        chunks.push(chunk);
    });

    req.on('end', function() {
        var buffer = Buffer.concat(chunks);

        req.bodyLength = buffer.length;
        req.rawBody = buffer;
        next();
    });

    req.on('error', function (err) {
        console.log(err);
        res.status(500);
    });
}

app.post('/upload-image', rawBody, function (req, res) {

    if (req.rawBody && req.bodyLength > 0) {

        // TODO save image (req.rawBody) somewhere

        // send some content as JSON
        res.send(200, {status: 'OK'});
    } else {
        res.send(500);
    }

});

我将尝试解释 node.js 部分: 函数 rawBody 充当 Express 中间件。当发出 POST 请求时,将使用请求对象调用此函数。它为 dataenderror 事件注册侦听器。 data 事件将所有传入的数据块附加到缓冲区。当 end 触发时,会在请求对象中创建 属性 rawBody 并包含二进制数据(您的图像 blob)。 rawBody() 然后将控制转移到下一个处理程序,该处理程序现在可以将 blob 保存到您的数据库或文件系统。

当处理真正的大数据块时,这种处理方式并不是最好的方法。最好将数据流式传输到文件或数据库以节省内存。

我的分数还不够,所以我必须写一个完整的答案而不是评论。

我想对 hgoebl 的回答补充一些内容,这很棒,节省了我很多时间!谢谢!

在清除流之前,您必须添加这些行,因为没有它们我无法让它工作。

urlConnection.setRequestProperty("Content-Type", "multipart/form-data");
urlConnection.setFixedLengthStreamingMode(1024);

如果您想直接从 USB/SD 存储中读取,则无需解码位图并将其编码为压缩格式。喜欢

// ... set request header 

FileInputStream fis = new FileInputStream(filePath);

OutputStream os = new BufferedOutputStream(urlConnection.getOutputStream());
OutputStreamWriter out = new OutputStreamWriter(os);

byte[] buffer = new byte[1024];
int read;
while ((read = fis.read(buffer)) != -1) {
    os.write(buffer, 0, read);
}

... // flush and close

有了这个你应该能够上传任何类型的二进制数据。