Laravel Storage SFTP and uploaded files permissions cmstest.com (ok)

C:\xampp8\htdocs\marinesTeam26CMS\.env

APP_NAME="Team26 CMS"
APP_ENV=local
APP_KEY=base64:vOBFzfwk4+/SWMoO0F3/xQxym4cph9aeHGJwEdiEARk=
APP_DEBUG=true
APP_URL=https://cmstest.com
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=cmstest
DB_USERNAME=root
DB_PASSWORD=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
SFTP_HOST=10.200.102.132
SFTP_USERNAME=hotfactory
SFTP_PASSWORD=cmshf2022
# SFTP_PRIVATE_KEY=~/.ssh/id_ecdsa

C:\xampp8\htdocs\marinesTeam26CMS\app\Http\Controllers\TopPageController.php

<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Carbon\Carbon;
use App\Models\Banner;
use App\Models\FanTypeNameEn;
use File;
use Storage;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use App\Helpers\LogActivity as LogActivityHelp;
use Illuminate\Support\Facades\Gate;
class TopPageController extends Controller
{
  /**
   * Create a new controller instance.
   *
   * @return void
   */
  public function __construct()
  {
    $this->middleware('auth');
  }
  public function top_page_type(Request $request, $id)
  {
    $datas = $request->all();
    $userid = Auth::user()->user_id;
    $username = Auth::user()->user_name;
    $keyword = array_key_exists("keyword", $datas) ? $datas['keyword'] : '';
    $membership = array_key_exists("membership", $datas) ? $datas['membership'] : [];
    $createUpdateAt = isset($datas['createUpdateAt']) ? $datas['createUpdateAt'] : 'id';
    $status = array_key_exists("status", $datas) ? $datas['status'] : [0, 1];
    $descAsc = array_key_exists("descAsc", $datas) ? $datas['descAsc'] : "asc";
    $banners = Banner::where('banner_type_code', $id)->where('title', 'LIKE', "%{$keyword}%")->with('fantypenameen');
    if (count($membership)) {
      $banners = $banners->whereHas('fantypenameen', function ($q) use ($membership) {
        $q->whereIn('fan_type_name_en.id', $membership);
      });
    }
    $banners = $banners->whereIn('status', $status)->orderBy($createUpdateAt, $descAsc);
    $banners = $banners->paginate(20);
    $fantynameen = FanTypeNameEn::all();
    return view('top-page.index')->with(compact('banners', 'datas', 'id', 'fantynameen'));
  }
  public function top_page_type_edit_id_get(Request $request, $type, $id)
  {
    $userid = Auth::user()->user_id;
    $username = Auth::user()->user_name;
    $banner = Banner::where('id', $id)->with('fantypenameen')->first();
    $permission = Auth::user()->permission;
    $fantynameen = FanTypeNameEn::all();
    return view('top-page.show', compact('type', 'id', 'banner', 'permission','fantynameen'));
  }
  public function top_page_type_edit_id_put(Request $request, $type, $id)
  {
    $userid = Auth::user()->user_id;
    $username = Auth::user()->user_name;
    $request->validate(
      [
        'title' => 'required',
        'image' => 'image|mimes:jpeg,png,jpg,gif,svg|max:22048',
        'sort' => 'required|numeric|digits_between:1,10',
        'url' => 'required',
        'select' => 'required',
        'checks' => 'required',
        'time1' => 'required|before:time2',
        'time2' => 'required',
        'status' => 'required'
      ],
      [
        'title.required' => '管理名称は必須です。入力してください。',
        'image.max' => '画像登録は必須です。画像をアップロードしてください。',
        'sort.required' => '表示順は必須です。表示順を入力してください。。',
        'sort.numeric' => '表示順は数字で入力してください。',
        'sort.digits_between' => '表示順の最大桁数は10桁までです。',
        'url.required' => '遷移先は必須です。遷移先を入力してください。',
        'select.required' => 'リンク設定は必須です。選択してください。',
        'checks.required' => '会員種別は必須です。選択してください。',
        'time1.required' => '開始日時は必須です。入力してください。',
        'time1.before' => '開始日時が終了日より後に設定されています。終了日より前に設定してください。',
        'time2.required' => '終了日時は必須です。入力してください。',
        'status.required' => '公開・非公開を選択してください。',
      ]
    );
    $banner = Banner::find($id);
    $host = request()->getHttpHost();
    $host = "https://" . $host;
    $permission = Auth::user()->permission;
    $now = Carbon::now()->toDateTimeString();
    $request->validate([
      'title' => 'required'
    ]);
    $imageName = "";
    $datas = $request->all();
    if (isset($datas['imageBefore']) && $datas['imageBefore'] !="") {
      $trimImg = trim(str_replace($host."/","",$banner->image_url));
      $imagePath = public_path($trimImg);
      if ($trimImg != "" && File::exists($imagePath)) {
        unlink($imagePath);
      }
      $banner->image_url = $datas['imageBefore'];
    }
    $banner->title = $datas['title'];
    $banner->sort_no = $datas['sort'];
    $banner->url = $datas['url'];
    $banner->target = $datas['select'] == 0 ? '_blank' : '_self';
    $banner->publish_start = $datas['time1'];
    $banner->publish_end = $datas['time2'];
    $banner->status = $datas['status'];
    if ($permission == "1" && array_key_exists("checkper", $datas) && $datas['checkper'] == 'on') {
      $banner->deleted_at = Carbon::now();
    } else if ($permission == "1" && !array_key_exists("checkper", $datas)) {
      $banner->deleted_at = null;
    }
    $banner->updated_at = $now;
    $banner->save();
    $datachecks = $datas['checks'];
    if (count($datachecks)) {
      $banner->fantypenameen()->sync($datachecks);
    }
    $success = 'You have successfully update image ' . $imageName;
    return redirect()->route('top-page-type', $type)->with('message', 'State saved correctly!!!');
  }
  public function top_page_type_create_get(Request $request, $type)
  {
    $fantynameen = FanTypeNameEn::all();
    return view('top-page.create')->with(compact('type','fantynameen'));
  }
  public function top_page_type_create_post(Request $request)
  {
    $userid = Auth::user()->user_id;
    $username = Auth::user()->user_name;
    // LogActivityHelp::addToLog($userid, $username);
    $banner = new Banner();
    $host = request()->getHttpHost();
    $host = "https://" . $host;
    $datas = $request->all();
    if(isset($datas['image'])) {
      $imageR = 'required|image|mimes:jpeg,png,jpg,gif,svg|max:22048';
    }else {
      $imageR = '';
    }
    $request->validate(
      [
        'title' => 'required',
        'image' => $imageR,
        'sort' => 'required|numeric|digits_between:1,10',
        'url' => 'required',
        'select' => 'required',
        'checks' => 'required',
        'time1' => 'required|before:time2',
        'time2' => 'required',
        'status' => 'required'
      ],
      [
        'title.required' => '管理名称は必須です。入力してください。',
        'image.max' => '画像登録は必須です。画像をアップロードしてください。',
        'sort.required' => '表示順は必須です。表示順を入力してください。。',
        'sort.numeric' => '表示順は数字で入力してください。',
        'sort.digits_between' => '表示順の最大桁数は10桁までです。',
        'url.required' => '遷移先は必須です。遷移先を入力してください。',
        'select.required' => 'リンク設定は必須です。選択してください。',
        'checks.required' => '会員種別は必須です。選択してください。',
        'time1.required' => '開始日時は必須です。入力してください。',
        'time1.before' => '開始日時が終了日より後に設定されています。終了日より前に設定してください。',
        'time2.required' => '終了日時は必須です。入力してください。',
        'status.required' => '公開・非公開を選択してください。',
      ]
    );
    $now = Carbon::now()->toDateTimeString();
    $banner->banner_type_code = $datas['id'];
    $banner->sort_no = $datas['sort'];
    $banner->title = $datas['title'];
    $banner->url = $datas['url'];
    $banner->target = $datas['select'] == 0 ? '_blank' : '_self';
    $banner->publish_start = $datas['time1'];
    $banner->publish_end = $datas['time2'];
    $banner->status = $datas['status'];
    $banner->create_user = Auth::user()->user_name;
    $banner->created_at = $now;
    $banner->updated_at = $now;
    $filesystem = Storage::disk('sftp');
    $filesystem->getDriver()->getAdapter()->setDirectoryPerm(0755);
     if (isset($datas['image'])) {
        $imageName = time() . '.' . $request->image->extension();
        $banner->image_url = $host . "/storage/images/" . $imageName;
        $request->file('image')->storeAs('images', $imageName, 'public');
        //Put file to Storage SFTP
        $filesystem->putFileAs('/app/public/images', $request->file('image'), $imageName);
     }else {
        if (isset($datas['imageAfter']) && $datas['imageAfter'] != null) {
          $url = $datas['imageAfter'];
          $name = substr($url, strrpos($url, '/') + 1);
          $namechange = time() . $name;
          $destinationPath = storage_path() . '/app/public/images';
          $trimImg = trim(str_replace($host."/","",$url));
          $imagePath = public_path($trimImg);
          if (File::exists($imagePath)) {
            File::copy(base_path() . "/storage/app/public/images/" . $name, base_path() . "/storage/app/public/images/" . $namechange);
            //Put file to Storage SFTP
            $filesystem->put('images/'.$namechange, File::get(storage_path('/app/public/images/'.$name)), 'public');
          }
          $banner->image_url = $host . "/storage/images/" . $namechange;
        }
     }
    $banner->save();
    $datachecks = $datas['checks'];
    if (count($datachecks)) {
      $banner->fantypenameen()->attach($datachecks);
    }
    return redirect()->route('top-page-type', $datas['id'])->with('message', 'State saved correctly!!!');
  }
  public function top_page_type_copy_get(Request $request, $type)
  {
    $userid = Auth::user()->user_id;
    $username = Auth::user()->user_name;
    $permission = Auth::user()->permission;
    $getData = $request->all();
    $fantynameen = FanTypeNameEn::all();
    return view('top-page.copy', compact('type', 'getData', 'permission','fantynameen'));
  }
}

C:\xampp8\htdocs\marinesTeam26CMS\config\filesystems.php

<?php
return [
    /*
    |--------------------------------------------------------------------------
    | Default Filesystem Disk
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default filesystem disk that should be used
    | by the framework. The "local" disk, as well as a variety of cloud
    | based disks are available to your application. Just store away!
    |
    */
    'default' => env('FILESYSTEM_DRIVER', 'local'),
    /*
    |--------------------------------------------------------------------------
    | Filesystem Disks
    |--------------------------------------------------------------------------
    |
    | Here you may configure as many filesystem "disks" as you wish, and you
    | may even configure multiple disks of the same driver. Defaults have
    | been setup for each driver as an example of the required options.
    |
    | Supported Drivers: "local", "ftp", "sftp", "s3"
    |
    */
    'disks' => [
        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],
        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],
        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
        ],
        'sftp' => [
            'driver' => 'sftp',
            'host' => env('SFTP_HOST'),
            'root' => '/var/www/site/current/storage',
            'port' => 22,
            'visibility' => 'public',
            'permPublic' => 0755,
            // 基本認証の設定
            'username' => env('SFTP_USERNAME'),
            'password' => env('SFTP_PASSWORD'),
            // 暗号化パスワードを使用するSSHキーベースの認証の設定
            'privateKey' => env('SFTP_PRIVATE_KEY'),
        ],
    ],
    /*
    |--------------------------------------------------------------------------
    | Symbolic Links
    |--------------------------------------------------------------------------
    |
    | Here you may configure the symbolic links that will be created when the
    | `storage:link` Artisan command is executed. The array keys should be
    | the locations of the links and the values should be their targets.
    |
    */
    'links' => [
        public_path('storage') => storage_path('app/public'),
    ],
];

C:\xampp8\htdocs\marinesTeam26CMS\app\Providers\AppServiceProvider.php

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Pagination\Paginator;
use Storage;
use League\Flysystem\Filesystem;
use League\Flysystem\Sftp\SftpAdapter;
class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Paginator::useBootstrap();
        $this->app['request']->server->set('HTTPS','on');
        Storage::extend('sftp', function ($app, $config) {
            return new Filesystem(new SftpAdapter($config));
        });
    }
}

C:\xampp8\htdocs\marinesTeam26CMS\composer.json

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "require": {
        "php": "^8.1",
        "doctrine/dbal": "^3.6",
        "fruitcake/laravel-cors": "^2.0",
        "guzzlehttp/guzzle": "^7.0.1",
        "jenssegers/agent": "^2.6",
        "laravel/framework": "^8.75",
        "laravel/sanctum": "^2.11",
        "laravel/tinker": "^2.5",
        "laravel/ui": "^3.4",
        "laravelcollective/html": "^6.3",
        "league/flysystem-sftp": "~1.0"
    },
    "require-dev": {
        "barryvdh/laravel-debugbar": "^3.7",
        "facade/ignition": "^2.5",
        "fakerphp/faker": "^1.9.1",
        "laravel/sail": "^1.0.1",
        "mockery/mockery": "^1.4.4",
        "nunomaduro/collision": "^5.10",
        "phpunit/phpunit": "^9.5.10"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi"
        ]
    },
    "extra": {
        "laravel": {
            "dont-discover": []
        }
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

Last updated