двоичные ответы с Serverless, Bref PHP и Slim с кодировкой CORS или base64

Я настраивал бессерверную (.com) установку с AWS, используя Bref (уровень PHP) для запуска MPDF для преобразования HTML в PDF с помощью AWS Lambda и API Gateway. Следуя инструкциям,.. вот настройка.

После долгой отладки выяснилось, что я могу:

  1. Добавить apiGatewayвариант в бессерверном файле, но я всегда возникает проблема CORS (т. е. классическийNo 'Access-Control-Allow-Origin' header is present). Я пробовал каждую комбинацию заголовков со стороны клиента / запроса и ответов со стороны сервера (см. Код ниже).

  2. ИЛИ, я не могу apiGatewayвариант, как показано ниже, но тогда у меня есть to base64encode my body (see $body = base64_encode($pdf);) или я получаю сообщение об ошибке Bref The Lambda response cannot be encoded to JSON (...) говоря, что нужно использовать apiGateway параметры.

Encoding the body is fine, but doesn't work well for direct download as I need to base64decode the data from the response in order to have the binary data.

Help.

serverless.yml: (notice the commented out apiGateway settings - read more for why)

 service: html2pdf
    
    provider:
        name: aws
        region: us-east-1
        runtime: provided
        stage: ${opt:stage, 'dev'}
        # apiGateway:    ## <======= We talk about this below
        #     binaryMediaTypes:
        #         - '*/*'
        # environment:
        #     BREF_BINARY_RESPONSES: 1
    
    plugins:
        - ./vendor/bref/bref
    
    functions:
        html2pdf:
            handler: index.php
            description: ''
            timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
            layers:
                - ${bref:layer.php-73-fpm} #-fpm
            events:
                - http:
                    path: /html2pdf
                    method: post
                    cors: true
    
       
    
    # Exclude files from deployment
    package:
        exclude:
            - 'node_modules/**'
            - 'tests/**'

The index.php (mentioned in the yml file above):

 <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use App\JsonParserMiddleware;
    
    use Psr\Http\Message\ResponseInterface as Response;
    use Psr\Http\Message\ServerRequestInterface as Request;
    use Slim\Factory\AppFactory;
    
    $app = AppFactory::create();
    $app->addMiddleware(new JsonParserMiddleware());
    
    $app->post("/html2pdf", function (Request $request, Response $response) {
        
        $data = $request->getParsedBody();
        $payload = [];
        $statusCode = 200;
        if ($data['data'] == null) {
            $payload['error'] = "HTML 64-bit encoded string is required";
            return response($response, $payload, 422);
        }
        $mpdf = new \Mpdf\Mpdf(['tempDir' => '/tmp']);
        $mpdf->WriteHTML(base64_decode($data['data']));
        
        $pdf = $mpdf->Output(null,"S");
    
        $filename = $data['title'];
    
        $body = base64_encode($pdf);
    
        $response->getBody()->write($body);
    
        $response->isBase64Encoded = true;
        
        $response = $response 
                    ->withHeader("Access-Control-Allow-Origin", "*")
                    ->withHeader('Access-Control-Allow-Credentials', "true")  
                    ->withHeader('Content-Type','application/pdf')
                    ->withHeader('Content-Disposition', sprintf('attachment; filename="%s"', $filename))
                    ->withStatus($statusCode);
        
        return $response;
        
    });

And the client-side code, using axios, is:

let data = { data: <<some base64 encoded html string>>, title: "file.pdf" };

let options = {
              headers: {
                "Content-Type": "application/json",
                // "Accept": "*/*",
              },
             }
let payload = {data : btoa(data),title:"contract.pdf"};
let url = "https://oli9w0wghb.execute-api.us-east-1.amazonaws.com/dev/html2pdf";

axios.post(url, payload, options)
    .then(response => {
      console.log(response.data);
    });

1 ответ

Привет, у меня была такая же проблема, и я ее исправил:

Заголовки, которые я использовал в контроллере (я использую SLIM4):

          $response->getBody()->write($pdfContent);
    $response = $response->withHeader('Content-type', 'application/pdf');
    $response->isBase64Encoded = true;

Затем, чтобы настроить serverless.yml, мне нужно добавить:

      service: *****

provider:
    region: ****
    runtime: ***
    profile: ****
    apiGateway:
        binaryMediaTypes:
            - 'application/pdf'
    environment:
        BREF_BINARY_RESPONSES: '1'
       


plugins:
    - ./vendor/bref/bref

functions:
    api:
        handler: public/index.php
        description: ''
        timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
        layers:
            - ${bref:layer.php-81-fpm}
        events:
            -   http: ANY /{proxy+}
        environment:
            BREF_BINARY_RESPONSE: '1'

Я надеюсь, это поможет вам.

Другие вопросы по тегам