【问题标题】:TokenMismatchException in laravel 5.4 with image upload带有图像上传的 laravel 5.4 中的 TokenMismatchException
【发布时间】:2018-01-02 12:06:17
【问题描述】:

我正在构建一个 Laravel 5.4 应用程序,让您可以将图像上传到每个已注册的条目。我正在使用干预图像包,但意识到我需要一种方法来启用图像裁剪和旋转(由于某种原因,iphone 图像在上传时会旋转),所以我决定使用 jquery 插件 Slim Cropper。我已将必要的文件添加到我的代码中,但无法成功上传图片。

Slim Cropper 提供了两种上传图片的方法:通过常规表单(提交后给我"TokenMismatchException in VerifyCsrfToken.php (line 68)")和仅显示“无法上传”消息的 ajax 表单。我已经尝试了两种不同的更改方式,但无法使其正常工作。我所有的类/控制器都检查身份验证,并且我尝试以我能想到的各种方式发送 csrf 令牌,都显示相同的错误。


更新:根据 cmets 中的建议,我在 <form> 之后移动了 csrf 令牌,我更新了输入文件名以匹配示例中的文件名,并尝试通过中间件进行调试,没有任何错误消息. TokenMismatchException 错误不再是问题,但是一旦提交表单,我就会收到错误 Constant expression contains invalid operations in Slim.php (line 106) for public static function saveFile($data, $name, $path = public_path('/uploads/mascotas-img/'), $uid = true)。仍然没有解决这个问题。

代码如下:

路线

Route::post('/mascotas/avatar', 'PetsController@avatar');

宠物控制器

use App\Slim;
public function avatar(Request $request)
{
    if ( $request->avatar )
    {
        // Pass Slim's getImages the name of your file input, and since we only care about one image, postfix it with the first array key
        $image = Slim::getImages('avatar')[0];
        $mascota_num = $image['meta']->petId;

        // Grab the ouput data (data modified after Slim has done its thing)
        if ( isset($image['output']['data']) )
        {
            // Original file name
            $name = $image['output']['name'];
            //$name = $request->input('mascota_num');

            // Base64 of the image
            $data = $image['output']['data'];

            // Server path
            $path = public_path('/uploads/mascotas-img/');

            // Save the file to the server
            $file = Slim::saveFile($data, $name, $path);

            // Get the absolute web path to the image
            $imagePath = public_path('/uploads/mascotas-img/' . $file['name']);

            DB::table('mascotas')
                ->where('num',$mascota_num)
                ->update(['foto' => $imagePath]);

            //$mascota->foto = $imagePath;
            //$mascota->save();
        }
    }

    return redirect()->back()->with('success', "User's profile picture has been updated!");
}

超薄类

namespace App;

abstract class SlimStatus {
    const Failure = 'failure';
    const Success = 'success';
}

class Slim {

    public static function getImages($inputName = 'slim') {

        $values = Slim::getPostData($inputName);

        // test for errors
        if ($values === false) {
            return false;
        }

        // determine if contains multiple input values, if is singular, put in array
        $data = array();
        if (!is_array($values)) {
            $values = array($values);
        }

        // handle all posted fields
        foreach ($values as $value) {
            $inputValue = Slim::parseInput($value);
            if ($inputValue) {
                array_push($data, $inputValue);
            }
        }

        // return the data collected from the fields
        return $data;

    }

    // $value should be in JSON format
    private static function parseInput($value) {

        // if no json received, exit, don't handle empty input values.
        if (empty($value)) {return null;}

        // The data is posted as a JSON String so to be used it needs to be deserialized first
        $data = json_decode($value);

        // shortcut
        $input = null;
        $actions = null;
        $output = null;
        $meta = null;

        if (isset ($data->input)) {
            $inputData = isset($data->input->image) ? Slim::getBase64Data($data->input->image) : null;
            $input = array(
                'data' => $inputData,
                'name' => $data->input->name,
                'type' => $data->input->type,
                'size' => $data->input->size,
                'width' => $data->input->width,
                'height' => $data->input->height,
            );
        }

        if (isset($data->output)) {
            $outputData = isset($data->output->image) ? Slim::getBase64Data($data->output->image) : null;
            $output = array(
                'data' => $outputData,
                'width' => $data->output->width,
                'height' => $data->output->height
            );
        }

        if (isset($data->actions)) {
            $actions = array(
                'crop' => $data->actions->crop ? array(
                    'x' => $data->actions->crop->x,
                    'y' => $data->actions->crop->y,
                    'width' => $data->actions->crop->width,
                    'height' => $data->actions->crop->height,
                    'type' => $data->actions->crop->type
                ) : null,
                'size' => $data->actions->size ? array(
                    'width' => $data->actions->size->width,
                    'height' => $data->actions->size->height
                ) : null
            );
        }

        if (isset($data->meta)) {
            $meta = $data->meta;
        }

        // We've sanitized the base64data and will now return the clean file object
        return array(
            'input' => $input,
            'output' => $output,
            'actions' => $actions,
            'meta' => $meta
        );
    }

    // $path should have trailing slash
    public static function saveFile($data, $name, $path = public_path('/uploads/mascotas-img/'), $uid = true) {

        // Add trailing slash if omitted
        if (substr($path, -1) !== '/') {
            $path .= '/';
        }

        // Test if directory already exists
        if(!is_dir($path)){
            mkdir($path, 0755);
        }

        // Let's put a unique id in front of the filename so we don't accidentally overwrite older files
        if ($uid) {
            $name = uniqid() . '_' . $name;
        }
        $path = $path . $name;

        // store the file
        Slim::save($data, $path);

        // return the files new name and location
        return array(
            'name' => $name,
            'path' => $path
        );
    }

    public static function outputJSON($status, $fileName = null, $filePath = null) {

        header('Content-Type: application/json');

        if ($status !== SlimStatus::Success) {
            echo json_encode(array('status' => $status));
            return;
        }

        echo json_encode(
            array(
                'status' => $status,
                'name' => $fileName,
                'path' => $filePath
            )
        );
    }

    /**
     * Gets the posted data from the POST or FILES object. If was using Slim to upload it will be in POST (as posted with hidden field) if not enhanced with Slim it'll be in FILES.
     * @param $inputName
     * @return array|bool
     */
    private static function getPostData($inputName) {

        $values = array();

        if (isset($_POST[$inputName])) {
            $values = $_POST[$inputName];
        }
        else if (isset($_FILES[$inputName])) {
            // Slim was not used to upload this file
            return false;
        }

        return $values;
    }

    /**
     * Saves the data to a given location
     * @param $data
     * @param $path
     */
    private static function save($data, $path) {
        file_put_contents($path, $data);
    }

    /**
     * Strips the "data:image..." part of the base64 data string so PHP can save the string as a file
     * @param $data
     * @return string
     */
    private static function getBase64Data($data) {
        return base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $data));
    }

}

图片提交表单(tokenmismatch错误)

<form action="{{ url('mascotas/avatar') }}" method="post" enctype="multipart/form-data">
    <div class="modal-body">
        <div class="slim" data-label="Agregar imagen aquí" data-size="400, 400" data-ratio="1:1" data-meta-pet-id="{{ $mascota->num }}">
            @if ( $mascota->foto )
                <img src="{{ url('/uploads/mascotas-img/'.$mascota->foto) }}" />
            @endif
            <input type="file" name="avatar" required />
            {{ csrf_field() }}
        </div>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
        <button type="submit" class="btn btn-red">Cambiar Foto</button>
    </div>
</form>

备用提交表单(错误消息)

<div class="modal-body">
    <div class="slim" data-label="Agregar imagen aquí" data-size="400, 400" data-ratio="1:1" data-service="{{ url('mascotas/avatar') }}" data-meta-pet-id="{{ $mascota->num }}">
        @if ( $mascota->foto )
            <img src="{{ url('/uploads/mascotas-img/'.$mascota->foto) }}" />
        @endif
        <input type="file" name="avatar" />
    </div>
</div>
<div class="modal-footer">
    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
    <button type="submit" class="btn btn-red">Cambiar Foto</button>
</div>

带有示例的超薄图像裁剪器网站http://slimimagecropper.com/


我通过laravel图像干预的原始上传表单,这在上传时没有问题,但非常想用上述之一替换。

<form enctype="multipart/form-data" action="{{ url('mascotas/foto') }}" method="POST">
    <div class="modal-body">
        <img class="mascota-avatar" src="{{ url('/uploads/mascotas-img/'.$mascota->foto) }}">
        <div class="clearfix"></div>
        <input type="file" name="foto">
        <input type="hidden" name="_token" value="{{ csrf_token() }}">
        <input type="hidden" name="mascota_num" value="{{ $mascota->num }}">
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
        <button type="submit" class="btn btn-red">Cambiar Foto</button>
    </div>
</form>

感谢您的帮助!

【问题讨论】:

  • _token 隐藏输入不在您的其他表单中,它用于 laravel 中的 CSRF 检查,laravel.com/docs/5.4/csrf,您是否尝试在 Slim 表单中包含该隐藏输入?
  • 是的,我试过了,但它给了我同样的错误,这就是为什么我不知道为什么它不起作用。
  • 尝试调试中间件,允许 url 通过 csrf 检查并将其添加到 $except 数组中的 verifyCsrfToken 中间件,如下所示: protected $except = [ '/pass/this/url' ];看看这是否有效,然后尝试找出导致它的原因,如果它通过了
  • 尝试将 csrf 放在
    标签之后 :) 希望这会有所帮助
  • 我已将文件输入名称更改为 slim(如 slim 示例)并在
    之后移动 csrf,现在发送请求时没有令牌不匹配错误但没有任何反应(没有上传图像) .我已经打开调试并添加了前面提到的 url,但我也没有收到任何消息。

标签: php laravel laravel-5.4


【解决方案1】:

您应该在每个表单中包含{{ csrf_field() }},对于 Ajax 表单,您可以将令牌作为标题发送。

【讨论】:

  • 已经尝试了所有我能想到的方式发送令牌,但我仍然收到同样的错误,不知道为什么。
  • 在控制台上检查您正在使用的插件(slim cropper)正在发送什么类型的请求,检查它是否确实是“post”
  • 刚检查,设置为post。
  • 您也可以检查您是否没有超过帖子的最大大小,当您发送的数据过多时,可能会导致令牌不匹配异常......
  • 最大帖子大小和上传大小都设置为8m,到目前为止我一直在尝试使用200kb以下的图像,无论如何谢谢!
猜你喜欢
  • 2017-11-21
  • 2017-09-22
  • 2023-03-28
  • 2018-07-11
  • 2019-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-02
相关资源
最近更新 更多