Azure函数-如何使用默认`run()`方法中的`MultipartHttpServletRequest`类? [英] Azure Functions - How to use the `MultipartHttpServletRequest` class from the default `run()` method?

查看:116
本文介绍了Azure函数-如何使用默认`run()`方法中的`MultipartHttpServletRequest`类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

   public HttpResponseMessage run(
            @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {

Azure Functions(Java)规范中,run()方法上只有HttpRequestMessage参数. 我需要声明并使用MultipartHttpServletRequest从multipart/data请求中获取文件. 我正在尝试,但看不到将HttpRequestMessag强制转换为MultipartHttpServletRequest的任何方法.

There's only HttpRequestMessage parameter on the run() method in the Azure Functions(Java) spec. I need to declare and use MultipartHttpServletRequest to fetch a file from the multipart/data request. I'm trying but cannot see any way to cast HttpRequestMessag to MultipartHttpServletRequest.

请给我一些建议.

HttpTrigger规范为:

The HttpTrigger spec is : https://docs.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation.httptrigger?view=azure-java-stable

----------------------- 更新 ---------------- ---------

----------------------- update -------------------------

上传的图像仍然损坏. 大小与原始大小完全相同,但看起来像这样:

The uploaded image is still corrupted. The size is exaclty same as the original one, but it seems like this :

我将粘贴整个代码.请检查一下.

I will paste the entire code. Please review it.

函数类来源:

public class HttpTriggerJava {
    private static final String storageConnectionString =
            "DefaultEndpointsProtocol=http;" +
                    "AccountName=00000;" +
                    "AccountKey=00000";

    @FunctionName("HttpTriggerJava")
    public HttpResponseMessage run(
            @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) throws Exception{

        context.getLogger().info("Java HTTP trigger processed a request.");

        CloudStorageAccount storageAccount = CloudStorageAccount.parse(storageConnectionString);
        CloudBlobClient blobClient = storageAccount.createCloudBlobClient();
        CloudBlobContainer container = blobClient.getContainerReference("contents");

        // here the "content-type" must be lower-case
        String contentType = request.getHeaders().get("content-type"); // Get content-type header

        String body = request.getBody().get(); // Get request body
        String boundary = contentType.split(";")[1].split("=")[1]; // Get boundary from content-type header
        int bufSize = 1024;
        InputStream in = new ByteArrayInputStream(body.getBytes()); // Convert body to an input stream
        MultipartStream multipartStream  = new MultipartStream(in, boundary.getBytes(), bufSize, null); // Using MultipartStream to parse body input stream
        boolean nextPart = multipartStream.skipPreamble();
        while(nextPart) {
            String header = multipartStream.readHeaders();
            System.out.println("");
            System.out.println("Headers:");
            System.out.println(header);
            System.out.println("Body:");
            if (header.contains("Content-Type: image/")) {
                int start = header.indexOf("filename=")+"filename=".length()+1;
                int end = header.indexOf("\r\n")-1;
                String filename = header.substring(start, end);
                System.out.println(filename);
                FileOutputStream fos = new FileOutputStream(filename);
                multipartStream.readBodyData(fos);

                File sourceFile = new File(filename);
                CloudBlockBlob blob = container.getBlockBlobReference(filename);
                blob.uploadFromFile(sourceFile.getAbsolutePath());

            } else {
                multipartStream.readBodyData(System.out);
            }
            System.out.println("");
            nextPart = multipartStream.readBoundary();
        }

        return request.createResponseBuilder(HttpStatus.OK).body("Success").build();
    }
}

HTML是:

<head>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$(document).ready(function () {
    $("#myFile").change(function() {
      readURL(this);
    });

    $("#submit").click(function (event) {
        event.preventDefault();

        var form = $('#form')[0];
        var data = new FormData(form);

        $("#submit").prop("disabled", true);

        $.ajax({
            type: "POST",
            enctype: 'multipart/form-data',
            url: $(form).attr('action'),
            data: data,
            processData: false,
            contentType: false,
            cache: false,
            timeout: 600000,
            success: function (data) {
                $("#result").text(data);
                console.log("SUCCESS : ", data);
                $("#submit").prop("disabled", false);
            },
            error: function (e) {
                $("#result").text(e.responseText);
                console.log("ERROR : ", e);
                $("#submit").prop("disabled", false);
            }
        });
    });
});
function readURL(input) {
  if (input.files && input.files[0]) {
    var reader = new FileReader();

    reader.onload = function(e) {
      $('#blah').attr('src', e.target.result).show();
    }
    reader.readAsDataURL(input.files[0]);
  }
}
</script>
</head>

<body>
    <form id=form
        action="http://doopediafunctiontest.azurewebsites.net/api/HttpTriggerJava?code=00000"
        method="post" enctype="multipart/form-data">
        <p>
            <br /> <br /> <strong>My file:</strong><br /> <input type="file" id="myFile" name="myFile">
            <br /><img id="blah" src="#" alt="your image" style="display:none" />
        </p>
        <input id=submit type="submit" value="upload to Blob Storage">
    </form>

    <div id=result></div>
</body>

我用十六进制编辑器比较原始图像和损坏的图像. 而且我发现一些随机十六进制更改为3f,这应该是原因.也许有一些编码问题.但是我该如何解决呢?

I compare the original image and the corrupted image by a hex editor. And I found some random hexes changed to 3f, it should be the reason. Maybe there's some encoding problem. But how can I fix this?

(请点击放大)

推荐答案

听起来您想通过Java的HTML表单将文件通过Java的Http触发器上传到Azure函数,如下所示.

It sounds like you want to upload a file to your Azure Function with Http Trigger in Java via a HTML form with multipart/form-data like below.

<form method="POST" enctype="multipart/form-data" action="https://<your function app>/api/HttpTrigger-Java">
  File to upload: <input type="file" name="upfile"><br/>
  Notes about the file: <input type="text" name="note"><br/>
  <br/>
  <input type="submit" value="Press"> to upload the file!
</form>

但是,没有任何类实现接口

However, there is not any class implements the interface HttpRequestMessage<T> and seems to not cast HttpRequestMessage to HttpServletRequest after I researched the source code of GitHub Repo Azure/azure-functions-java-library.

根据我的经验,唯一的方法是解析multipart/form-data请求的标头和正文以获取文件.有一个类似的SO线程的答案库和示例从问题所有者发布的输入流中解析multipart/form-data的信息,其中包括使用

Per my experience, the only way is to parse the header and body of a multipart/form-data request to get the file. There is an answer of the similar SO thread Library and examples of parsing multipart/form-data from inputstream posted by the question owner, which includes the code using MultipartStream class of Apache Commons FileUpload that works after I test it.

这是从Azure函数for Java接收的Content-Type标头和multipart/form-data请求的正文.

Here is the Content-Type header and body of a multipart/form-data request received from Azure Function for Java.

标题Content-Type

Header Content-Type

content-type: multipart/form-data; boundary=----WebKitFormBoundaryT2TWuevX3RIYWRQF

multipart/form-data请求正文

multipart/form-data request body

------WebKitFormBoundaryT2TWuevX3RIYWRQF
Content-Disposition: form-data; name="upfile"; filename="z.txt"
Content-Type: text/plain
1234
ABCD
------WebKitFormBoundaryT2TWuevX3RIYWRQF
Content-Disposition: form-data; name="note"
test.txt
------WebKitFormBoundaryT2TWuevX3RIYWRQF--

这是我提取文件的示例代码.

Here is my sample code to fetch the file.

@FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
        final ExecutionContext context) {
    String contentType = request.getHeaders().get("content-type"); // Get content-type header
    // here the "content-type" must be lower-case
    String body = request.getBody().get(); // Get request body
    InputStream in = new ByteArrayInputStream(body.getBytes()); // Convert body to an input stream
    String boundary = contentType.split(";")[1].split("=")[1]; // Get boundary from content-type header
    int bufSize = 1024;
    MultipartStream multipartStream  = new MultipartStream(in, boundary.getBytes(), bufSize, null); // Using MultipartStream to parse body input stream
    // the code below comes from the SO thread above
    // you can fetch a file content from readBodyData 
    // after the headers Content-Disposition: form-data; name="upfile"; filename="test.txt" \n Content-Type: text/plain
    boolean nextPart = multipartStream.skipPreamble();
    while (nextPart) {
        String header = multipartStream.readHeaders();
        System.out.println("");
        System.out.println("Headers:");
        System.out.println(header);
        System.out.println("Body:");
        multipartStream.readBodyData(System.out);
        System.out.println("");
        nextPart = multipartStream.readBoundary();
    }
    return request.createResponseBuilder(HttpStatus.OK).body("Success").build();
}

上面的代码在终端中的输出:

The output of code above in terminal :

Headers:
Content-Disposition: form-data; name="upfile"; filename="test.txt"
Content-Type: text/plain


Body:
1234
ABCD


Headers:
Content-Disposition: form-data; name="note"


Body:
test.txt


更新:如果上传图片,则上面代码的输出如下所示.


Update: If upload an image, the output of the code above is like below.

Headers:
Content-Disposition: form-data; name="upfile"; filename="test.jpg"
Content-Type: image/png


Body:
<the binary content of an image>

因此,您可以解析标头以获取filename值,以使用FileOutputStream进行存储,如下所示.

So you can parse the header to get the filename value to use FileOutputStream to store it, as the code below.

while(nextPart) {
    String header = multipartStream.readHeaders();
    System.out.println("");
    System.out.println("Headers:");
    System.out.println(header);
    System.out.println("Body:");
    if (header.contains("Content-Type: image/")) {
        int start = header.indexOf("filename=")+"filename=".length()+1;
        int end = header.indexOf("\r\n")-1;
        String filename = header.substring(start, end);
        System.out.println(filename);
        FileOutputStream fos = new FileOutputStream(filename);
        multipartStream.readBodyData(fos);
    } else {
        multipartStream.readBodyData(System.out);
    }
    System.out.println("");
    nextPart = multipartStream.readBoundary();
}


更新2:


Update 2:

我发现似乎存在用于Java的Azure函数的问题,该问题可能是一个错误,该错误在上载二进制文件时会丢失一些字节,但在上载文本文件时不会发生.因此,一种解决方法是在浏览器中将上传文件转换为base64字符串,以发布到Azure Function,并将上传到base64的内容转换为Azure Function中的原始二进制文件.

I discovered there seems to be an issue of Azure Function for Java which may be a bug that will lose some bytes when uploading binary file, but it will not happend for uploading text file. So a workaround solution is to convert upload file to base64 string in browser to post to Azure Function and convert base64 content uploaded to the origin binary file in Azure Function.

这是我正在测试的HTML代码.

Here is my testing HTML code.

File to upload: <input type="file" name="upfile" id="fileup"><br/>
<form method="POST" enctype="multipart/form-data" action="http://localhost:7071/api/HttpTrigger-Java">
  Notes about the file: <input type="text" name="note"><br/>
  <input type="hidden" name="file_base64" id="file_base64"><br/>
  <input type="submit" value="Press"> to upload the file!
</form>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">/script>
<script>
$(document).ready(function(){
    $("#fileup").change(function(){
        var v = $(this).val();
        var reader = new FileReader();
        reader.readAsDataURL(this.files[0]);
        reader.onload = function(e){
            console.log(e.target.result);
            $('#file_base64').val(e.target.result);
        };
    });
});
</script>

上面的表格将如下所示发布base64文件块的标题和正文.

The form above will post the header and body of base64 file chunk as below.

Header:
Content-Disposition: form-data; name="file_base64"
Body:
data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAB.............

我在Azure函数中的Java代码:

My Java code in Azure Function:

import java.io.ByteArrayOutputStream;
import java.util.Base64;

if (header.equals("Content-Disposition: form-data; name=\"file_base64\"")) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    multipartStream.readBodyData(baos);
    String content = baos.toString();
    // System.out.println(content);
    int index = content.indexOf(",")+1; // Get the index of base64 string in data-uploaded string
    byte[] imgBytes = Base64.getDecoder().decode(content.substring(index)); // convert image base64 string to image byte arrays
    ....
    // To upload image byte array to Blob Storage
    // You can get the upload image filename from the form input `note`, please notes the order of form input elements.
} else {
    multipartStream.readBodyData(System.out);
}

这篇关于Azure函数-如何使用默认`run()`方法中的`MultipartHttpServletRequest`类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆