如何在 Laravel 里实现文件转发

1.需求

现有两台服务器:laravel 项目所在服务器(服务器A)、备份服务器(服务器B),目标是将用户上传的图片保存在服务器A的同时备份至服务器B

2.实现方式

我使用zttp扩展配合CURLFile将服务器A的文件发送至服务器B

服务器A转发代码块

$url = "http://api.test/api/v1/images/bak";
$data = [
    'image'       => new \CURLFile($upload_path."/".$file_name,$extension),
    'folder_name' => $folder_name,
    'file_name'   => $file_name
 ];

$response = Zttp::asFormParams()->withHeaders([
    'X-Requested-With'=>'XMLHttpRequest'
])->post($url,$data);
dd($response->json());

服务器B接收代码块

return $request->file('image');

3.问题

服务器B返回的数据为null,打印$_FILES数组也没有数据,这是为什么

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

我想你应该是想解决分布式部署的laravel项目,上传的话只能上传到一个服务器,获取图片的时候负载均衡到另外一台服务器就获取不到图片的情况吧。首先解决一下你的问题:试一下使用asMultipart()而不是asFormParams();。

然后,解决这种问题其实有更好的办法:

  1. 我首推的办法是使用“对象存储”,数据库里仅保存云端对象的存储路径。搭配CDN使用,一般来讲也是比较正规的处理方法。
  2. 其次,对于暂时没办法使用“对象存储”方案的项目,可以使用rsync进行同步,rsync同步的方式比接口上传文件的方式速度快而且效率高,只需要简单配置就可以了,可以了解一下。

以上,可以参考下。

3年前 评论
xch_q (楼主) 3年前
讨论数量: 2

我想你应该是想解决分布式部署的laravel项目,上传的话只能上传到一个服务器,获取图片的时候负载均衡到另外一台服务器就获取不到图片的情况吧。首先解决一下你的问题:试一下使用asMultipart()而不是asFormParams();。

然后,解决这种问题其实有更好的办法:

  1. 我首推的办法是使用“对象存储”,数据库里仅保存云端对象的存储路径。搭配CDN使用,一般来讲也是比较正规的处理方法。
  2. 其次,对于暂时没办法使用“对象存储”方案的项目,可以使用rsync进行同步,rsync同步的方式比接口上传文件的方式速度快而且效率高,只需要简单配置就可以了,可以了解一下。

以上,可以参考下。

3年前 评论
xch_q (楼主) 3年前

送你一套 httptrait :grin:

参照 postFile 方法即可

<?php

namespace api\helper;

use GuzzleHttp\Client;
use Psr\Http\Message\ResponseInterface;

/**
 * Trait HasHttpRequest.
 */
trait HttpClient
{
    /**
     * Make a get request.
     *
     * @param string $endpoint
     * @param array $query
     * @param array $headers
     *
     * @return array
     */
    protected function get($endpoint, $query = [], $headers = [])
    {
        return $this->request('get', $endpoint, [
            'headers' => $headers,
            'query'   => $query,
        ]);
    }

    /**
     * Make a post request.
     *
     * @param string $endpoint
     * @param array $params
     * @param array $headers
     *
     * @return array
     */
    protected function post($endpoint, $params = [], $headers = [])
    {
        return $this->request('post', $endpoint, [
            'headers'     => $headers,
            'form_params' => $params,
        ]);
    }

    /**
     * 上传文件需要特殊处理
     * @param $endpoint
     * @param array $params
     * @param array $headers
     * @return array
     */
    protected function postFile($endpoint, $params, $headers = [])
    {
        $data = $this->createMultipart($params);
        foreach ($_FILES as $name => $FILE) {
            $data[] = [
                'name'     => $name,
                'filename' => $FILE['name'],
                'contents' => fopen($FILE['tmp_name'], 'r'),
            ];
        }

        return $this->request('post', $endpoint, [
            'headers'   => $headers,
            'multipart' => $data,
        ]);
    }

    /**
     * guzzle 上传文件多维数组需要处理
     * @param array $parameters
     * @param string $prefix
     * @return array
     */
    protected function createMultipart(array $parameters, $prefix = '')
    {
        $return = [];
        foreach ($parameters as $name => $value) {
            $item = [
                'name' => empty($prefix) ? $name : "{$prefix}[{$name}]",
            ];
            switch (true) {
                case (is_object($value) && ($value instanceof CURLFile)):
                    $item['contents'] = fopen($value->getFilename(), 'r');
                    $item['filename'] = $value->getPostFilename();
                    $item['headers'] = [
                        'content-type' => $value->getMimeType(),
                    ];
                    break;
                case (is_string($value) && is_file($value)):
                    $item['contents'] = fopen($value, 'r');
                    break;
                case is_array($value):
                    $return = array_merge($return, $this->createMultipart($value, $item['name']));
                    continue 2;
                default:
                    $item['contents'] = $value;
            }
            $return[] = $item;
        }

        return $return;
    }

    /**
     * Make a post request with json params.
     *
     * @param       $endpoint
     * @param array $params
     * @param array $headers
     *
     * @return array
     */
    protected function postJson($endpoint, $params = [], $headers = [])
    {
        return $this->request('post', $endpoint, [
            'headers' => $headers,
            'json'    => $params,
        ]);
    }

    /**
     * Make a http request.
     *
     * @param string $method
     * @param string $endpoint
     * @param array $options http://docs.guzzlephp.org/en/latest/request-options.html
     *
     * @return array
     */
    protected function request($method, $endpoint, $options = [])
    {
        try {
            $ret = $this->unwrapResponse($this->getHttpClient($this->getBaseOptions())->{$method}($endpoint, $options));
            // 换成你自己的 log
            WebClientLog::log([
                'request'  => [
                    'url'  => $endpoint,
                    'data' => $options,
                ],
                'response' => $ret,
            ], __CLASS__);

            return $ret;
        } catch (\Exception $e) {
            // 换成你自己的 log
            WebClientLog::log([
                'request'  => [
                    'url'  => $endpoint,
                    'data' => $options,
                ],
                'response' => '网络异常:' . $e->getMessage(),
            ], __CLASS__);
        }
    }

    /**
     * Return base Guzzle options.
     *
     * @return array
     */
    protected function getBaseOptions()
    {
        $options = [
            'base_uri' => method_exists($this, 'getBaseUri') ? $this->getBaseUri() : '',
            'timeout'  => method_exists($this, 'getTimeout') ? $this->getTimeout() : 10.0, // 默认超时 10 秒钟
        ];

        return $options;
    }

    /**
     * Return http client.
     *
     * @param array $options
     *
     * @return Client
     *
     * @codeCoverageIgnore
     */
    protected function getHttpClient(array $options = [])
    {
        return new Client($options);
    }

    /**
     * Convert response contents to json.
     *
     * @param ResponseInterface $response
     *
     * @return ResponseInterface|array|string
     */
    protected function unwrapResponse(ResponseInterface $response)
    {
        $contentType = $response->getHeaderLine('Content-Type');
        $contents = $response->getBody()->getContents();

        if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) {
            return json_decode($contents, true);
        } elseif (false !== stripos($contentType, 'xml')) {
            return json_decode(json_encode(simplexml_load_string($contents)), true);
        }

        $contents = is_null(json_decode($contents, true)) ? $contents : json_decode($contents, true);

        return $contents;
    }
}
3年前 评论
xch_q (楼主) 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!