提问者:小点点

Azure Functions-如何使用默认“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的方法。

请给我一些建议。

HttpTrigger规范为:https://learn . Microsoft . com/en-us/Java/API/com . Microsoft . azure . functions . annotation . http trigger?view=azure-java-stable

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

上载的图像仍然损坏。尺寸与原始尺寸完全相同,但看起来是这样的:

我将粘贴整个代码。请查看它。

函数类源:

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,这应该是原因。也许有一些编码问题。但我该怎么解决呢?


共2个答案

匿名用户

听起来你想用Java中的Http Trigger通过HTML表单上传一个文件到你的Azure函数,该表单包含如下<code>multipart/form data</code>。

<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>

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

根据我的经验,唯一的方法是解析< code>multipart/form-data请求的头和体来获取文件。有一个类似SO thread库的回答和问题所有者发布的从inputstream解析multipart/form-data的例子,其中包括使用Apache Commons FileUpload的< code>MultipartStream类的代码,在我测试后可以工作。

以下是从Azure Function for Java收到的< code>multipart/form-data请求的< code>Content-Type头和正文。

标题Content-Type

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

多部分/表单数据请求正文

------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--

这是我获取文件的示例代码。

@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();
}

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

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

更新:如果上传图片,上面代码的输出如下。

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


Body:
<the binary content of an image>

因此,您可以解析标头以获取< code>filename值,并使用< code>FileOutputStream来存储它,如下面的代码所示。

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:

我发现似乎有一个Java的Azure函数的问题,这可能是一个错误,当上传二进制文件时会丢失一些字节,但上传文本文件时不会发生。因此,一个变通的解决方案是在浏览器中将上传文件转换为base64字符串以发布到Azure函数,并在Azure函数中转换上传到原始二进制文件的base64内容。

这是我测试的超文本标记语言代码。

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文件块的标头和正文,如下所示。

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

我在 Azure 函数中的 Java 代码:

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

匿名用户

为什么不用

HttpRequestMessage<Optional<byte[]>> request

代替

HttpRequestMessage<Optional<String>> request

先把身体改造成弦把事情弄糟。