在PHP/Laravel/Docker中使用php-pm进行图片上传遇到的问题

背景

我使用Vue/PHP开发了一个图片上传的网站。

请提出问题。

在本地内置服务器中,可以进行图片上传,但是在执行request->file时,在Docker启动的内置服务器中进行图片上传,会显示validation:update,并且无法进行图片上传。

结论

由于在php-pm中未将tmp文件保存在/private/var/目录下,所以我们决定使用php-fpm。

前提 – premise

我在Laravel的store方法中添加了Log来确认并上传了一个被认为是可疑的日志输出图像。

php-pm 可以将 PHP 应用程序作为持久运行的进程执行,而不是每个请求都重新启动。

FROM phppm/nginx:2.0.3
RUN apk update && apk add --no-cache \
      tzdata curl \
      && apk del tzdata \
      && rm -rf /var/cache/apk/*
COPY . /var/www
CMD ["--bootstrap=laravel","--static-directory=public/"]

Laravel方面

public function store(Request $request)
    {
        Log::debug('API storeメソッドの実行');
        Log::debug($request);
        Log::debug(request());

        $this->validate($request, [
            'file' => 'required|image',
            'gear_name' => 'required',
            'gear_category' => 'required',
            'maker_name' => 'required',
            'content' => ['required', 'min:2'],

        ], [
            'file.required' => '画像が選択されていません',
            'file.image' => '画像ファイルではありません',
            'gear_category.required' => '登録するギアのカテゴリを選択してください',

            'gear_name.required' => '登録するギアの名前を入力してください',
            'maker_name.required' => '登録するギアのメーカ名を入力してください',
            'content.min' => 'ギアのお気に入りポイントを2文字以上入力してください',
            'content.required' => 'ギアのお気に入りポイントを入力してください',
        ]);
        Log::debug('$image = $request->file');
        Log::debug($image = $request->file('file'));
        Log::debug('=====');
        if ($request->hasFile('file')) {
            Log::debug('requstにfileがある時の処理');
            Log::debug(request()->file);

            $image = $request->file('file');
            $image_url = Storage::disk('s3')->put('/myprefix', $image, 'public');
            $gear = new Gear();
            $gear->image_url = Storage::disk('s3')->url($image_url);
            $gear->gear_name = $request->gear_name;
            $gear->maker_name = $request->maker_name;
            $gear->content = $request->content;
            $gear->ref_url = self::replaceUrl($request->ref_url);
            $gear->gear_category = $request->gear_category;
            $gear->updated_at = date('Y/m/d H:i:s');
            $gear->gear_category = $request->gear_category;

            $gear->user_id = $request->user_id;
            $gear->save();

            return ['success' => '登録しました!'];
        }
    }

Vue側可以表示为”Vue方面”或”Vue端”。

<script>
        //画像、コメント投稿用のvue側のaxios通信
        uploadImage() {
            let data = new FormData();
            data.append("file", this.file);
            data.append("maker_name", this.maker_name);
            data.append("gear_name", this.gear_name);
            data.append("gear_category", this.gear_category);
            data.append("content", this.content);
            //Vuexのstoreからauth_user情報を呼び出す
            data.append("user_id", this.$store.state.auth_user.id);
            console.log('dataの表示')
            console.log(data);
            for (let value of data.entries()) {
                console.log(value);

            }
            NProgress.start();
            axios.post("/api/gears", data)
            // axios.post("/gears", data)
                .then(response => {
                    setTimeout(()=>{
                    // this.getGears();
                    console.log('responseの表示')
                    console.log(response);
    /***
       responseは表示されない
     ***/
                    this.message = response.data.success;
                    this.confirmedImage = "";
                    this.maker_name = "";
                    this.gear_name = "";
                    this.gear_category = "";
                    this.content = "";
                    this.file = "";
                    // this.user_id = $route.params;
                    this.message = 'success'
                        //ファイルを選択のクリア
                    this.view = false;
                    this.is_post_success = true;
                    this.$nextTick(function () {
                        this.view = true;
                    });
                    NProgress.done();
                },3000);
                })
                .catch(err => {
                    console.log('errorの表示')
                    console.log(err)
                    console.log(err.response)
   /***
    Error: Request failed with status code 422
       at createError (app.js:699)
        at settle (app.js:960)
       at XMLHttpRequest.handleLoad (app.js:168)
    {data: {…}, status: 422, statusText: "", headers: {…},     
    config: {…}, …}
     config: {url: "/api/gears", method: "post", data: 
   FormData, headers: {…}, transformRequest: Array(1), …}
    data: {message: "The given data was invalid.", errors: {…}}
   headers: {access-control-allow-origin: "*", cache-control: "no- cache, private", content-length: "83", content-type: 
   "application/json", date: "Thu, 21 Jan 2021 10:50:37 GMT", …}
    request: XMLHttpRequest {readyState: 4, timeout: 0, 
    withCredentials: false, upload: XMLHttpRequestUpload, 
    onreadystatechange: ƒ, …}
    status: 422
    statusText: ""
    __proto__: Object
      ***/
                    this.message = err.response.data.errors;
                    this.is_post_success = false;
                });
        }
        //    ここまで
    }
};
</script>

确认错误

[2021-01-22 19:04:39] production.DEBUG: API storeメソッドの実行  
[2021-01-22 19:04:39] production.DEBUG: array (
  'maker_name' => 'da',
  'gear_name' => 'da',
  'gear_category' => 'BackPack',
  'content' => 'da',
  'user_id' => '1',
  'file' => 
  Illuminate\Http\UploadedFile::__set_state(array(
     'test' => false,
     'originalName' => 'テスト.png',
     'mimeType' => 'image/png',
     'error' => 0,
     'hashName' => NULL,
  )),
)  
[2021-01-22 19:04:39] production.DEBUG: requstにfileがある時の処理  
[2021-01-22 19:04:39] production.DEBUG: /private/var/tmp/phpRMqJe0  

请求为/private/var/tmp/phpRM

正在显示。

[2021-01-22 19:06:35] dev.DEBUG: index                         
[2021-01-22 19:06:42] dev.DEBUG: API store.....................
[2021-01-22 19:06:42] dev.DEBUG: array (          
  'maker_name' => 'da',                           
  'gear_name' => 'da',                            
  'gear_category' => 'BackPack',                                                
  'content' => 'daa',                                                           
  'user_id' => '1',                                                             
  'file' =>                                                                     
  Illuminate\Http\UploadedFile::__set_state(array(                              
     'test' => false,                                                           
     'originalName' => '..........png',                                         
     'mimeType' => 'image/png',                                                 
     'error' => 2190,                                                           
     'hashName' => NULL,                                                        
  )),                                                                           
)                                                                               

[2021-01-23 20:47:52] production.DEBUG: array (
  'maker_name' => 'ss',
  'gear_name' => 'sss',
  'gear_category' => 'Cutting',
  'content' => 'ss',
  'user_id' => '1',
  'file' =>
  Illuminate\Http\UploadedFile::__set_state(array(
     'test' => false,
     'originalName' => 'テスト.png',
     'mimeType' => 'image/png',
     'error' => 0,
     'hashName' => NULL,
  )),
)
[2021-01-23 20:47:52] production.DEBUG: $image = $request->file
[2021-01-23 20:47:52] production.DEBUG: /tmp/php4eELue
[2021-01-23 20:47:52] production.DEBUG: =====
[2021-01-23 20:47:52] production.DEBUG: requstにfileがある時の処理
[2021-01-23 20:47:52] production.DEBUG: /tmp/php4eELue

在正常情况下,它会被写入到/tmp文件夹中。

猜测

在本地环境中,request()->file()函数会有返回值,而在Docker中使用php-pm时,该函数没有返回值。而在Docker中使用php-fpm时,request()->file()函数会有返回值。

在php-pm中,考虑到/tmp目录及其以下没有写入权限,甚至可能根本就没有该目录。

解决方案

我已经准备好了一个包含php-fpm的Dockerfile。

#php 7.3 fpmを仕様
FROM php:7.3-fpm 
# 設定ファイルphp.iniをコピーする。
# Dockerfileにあるディレクトリをコピーして/var/wwwに保存

COPY ./docker/php/php.ini /usr/local/etc/php/
COPY  ./ /var/www/ 

#必要なファイルを保存する。
RUN apt-get update \
  && apt-get install -y zlib1g-dev mariadb-client \
  && apt-get install -y libzip-dev \
  && docker-php-ext-install zip pdo_mysql

#作業ディレクトリを/var/wwwに設定する。
WORKDIR /var/www

結果/結論是這樣的/成果

除非有特别的理由,否则不要去碰搞不清楚使用方式的php-pm,我们应该使用php-fpm。

以上

广告
将在 10 秒后关闭
bannerAds