Laravel使用ftp传输文件时报错ftp_put() No data connection的解决
发表于|更新于|码不能停
|总字数:155|阅读时长:1分钟|浏览量:
代码:
1 | <?php |
原因是没有定义ftp的主被动模式,true是被动模式:
1 | ftp_pasv($cid, true); |
文章作者: m-finder
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 M-finder!
相关推荐

2019-03-15
Laravel 生命周期
Laravel 的生命周期主要分为四个阶段: 加载依赖 创建应用实例 接收请求并响应 请求结束进行回调 这四个阶段都在 index.php 中完成: 1234567891011121314151617181920<?php// 加载依赖require __DIR__.'/../vendor/autoload.php';// 创建应用实例$app = require_once __DIR__.'/../bootstrap/app.php';// 实例化 HTTP 内核$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);// 接收请求,生成响应$response = $kernel->handle( $request = Illuminate\Http\Request::capture());// 发送响应$response->send();// 请求结束,进行回调$kernel->terminate($request, $response); 1. 加载依赖laravel 框架依赖 composer 管理扩展包,通过引入 composer 的自动加载程序,就可以轻松完成扩展加载: 1require __DIR__.'/../vendor/autoload.php'; 2 创建应用实例这一步主要由以下几个小步骤组成: 创建应用实例 完成基础注册 基础绑定 基础服务提供者注册 event log route 核心类别名注册 绑定核心 创建应用实例,由 bootstrap/app.php 完成,然后注册三个核心。 1234567<?php// 第一部分: 创建应用实例$app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../'));…… 2.1 完成基础注册应用实例创建后,再来看一下具体是怎么工作的,打开 Illuminate\Foundation\Application,其代码如下: 1234567891011121314public function __construct($basePath = null){ // 应用的路径绑定 if ($basePath) { $this->setBasePath($basePath); } // 将基础绑定注册到容器中,容器名 $this->registerBaseBindings(); // 将基础服务提供者注册到容器 Event、Log、Route $this->registerBaseServiceProviders(); // 将核心类别名注册到容器 $this->registerCoreContainerAliases();} 2.2 内核绑定接着看 bootstrap/app.php 中的代码: 12345678910111213141516171819……// 第二步,内核绑定$app->singleton( Illuminate\Contracts\Http\Kernel::class, App\Http\Kernel::class);$app->singleton( Illuminate\Contracts\Console\Kernel::class, App\Console\Kernel::class);$app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class); 绑定三个内核,HTTP、Console、Exception内核。 3 接收请求并响应再次回到 index.php,查看请求和响应的相关代码: 1234567891011……$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle( $request = Illuminate\Http\Request::capture());$response->send();…… 这一步也是由几个小步骤组成: 实例化 HTTP 核心 实例化内核 注册中间件到路由 session 共享错误 身份验证请求 …… 请求处理 创建请求实例 处理请求,返回响应 发送响应 3.1 注册中间件到路由在 Illuminate\Contracts\Http\Kernel::class 类的构造方法中,将在 HTTP 内核定义的「中间件」注册到路由,注册完后就可以在实际处理 HTTP 请求前调用这些「中间件」实现过滤请求的目的。 1234567891011121314151617181920212223242526272829303132protected $middlewarePriority = [ \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class,];public function __construct(Application $app, Router $router){ $this->app = $app; $this->router = $router; $this->syncMiddlewareToRouter();}// 注册中间件到路由protected function syncMiddlewareToRouter(){ $this->router->middlewarePriority = $this->middlewarePriority; foreach ($this->middlewareGroups as $key => $middleware) { $this->router->middlewareGroup($key, $middleware); } foreach ($this->routeMiddleware as $key => $middleware) { $this->router->aliasMiddleware($key, $middleware); }} 3.2 处理请求处理请求实际包含两个阶段: 创建请求实例 处理请求 3.2.1 创建请求实例通过 Symfony 实例创建一个 Laravel 请求实例。 12345678910111213141516171819202122public static function capture(){ static::enableHttpMethodParameterOverride(); return static::createFromBase(SymfonyRequest::createFromGlobals());}public static function createFromBase(SymfonyRequest $request){ $newRequest = (new static)->duplicate( $request->query->all(), $request->request->all(), $request->attributes->all(), $request->cookies->all(), $request->files->all(), $request->server->all() ); $newRequest->headers->replace($request->headers->all()); $newRequest->content = $request->content; $newRequest->request = $newRequest->getInputSource(); return $newRequest;} 3.2.2 处理请求在 HTTP 核心的 handdle 方法内,接收一个请求,也就是上一步创建的请求实例,最终生成一个响应。 主要步驟如下: 注册请求到容器 运行引导程序 环境检测,将 env 中的配置读取到变量中 配置文件加载 加载异常处理 注册门面 注册服务提供者 服务启动 发送请求到路由 查找路由 运行控制器或匿名函数 返回响应 HTTP 核心的 handle 方法: 123456789101112131415161718public function handle($request){ try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Throwable $e) { $this->reportException($e); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new RequestHandled($request, $response) ); return $response;} 再往下深入,查看 $response = $this->sendRequestThroughRouter($request); 的具体实现: 12345678910111213141516protected function sendRequestThroughRouter($request){ // 将请求注册到容器 $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); // 启动引导程序 $this->bootstrap(); // 发送请求至路由 return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter());} 首先,将 request 注册到容器内,然后清除掉之前的 request 实例缓存,启动引导程序,然后将请求发送到路由。 接下来,看一下引导程序是做什么的: 12345678910111213141516171819202122232425262728293031323334protected $bootstrappers = [ \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, \Illuminate\Foundation\Bootstrap\HandleExceptions::class, \Illuminate\Foundation\Bootstrap\RegisterFacades::class, \Illuminate\Foundation\Bootstrap\RegisterProviders::class, \Illuminate\Foundation\Bootstrap\BootProviders::class,];public function bootstrap(){ if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); }}protected function bootstrappers(){ return $this->bootstrappers;}// src/Illuminate/Foundation/Application.phppublic function bootstrapWith(array $bootstrappers){ $this->hasBeenBootstrapped = true; foreach ($bootstrappers as $bootstrapper) { $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]); $this->make($bootstrapper)->bootstrap($this); $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]); }} 在容器内的具体实现方法中,会先解析引导程序,然后再通过调用引导程序的 bootstrap 方法来启动服务。引导程序功能: 环境检测,将 env 配置文件载入到 $_ENV 变量中 加载配置文件 加载异常处理 加载 Facades,注册完成后可以用别名的方式访问具体的类 注册服务提供者,在这里我们会将配置在 app.php 文件夹下 providers 节点的服务器提供者注册到 APP 容器,供请求处理阶段使用 服务启动 在发送请求至路由这行代码中,完成了:管道(pipeline)创建、将 request 传入管道、对 request 执行中间件处理和实际的请求处理四个不同的操作。 1234return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); 继续深入 $this->dispatchToRouter(),分析程序是如何处理请求的: 注册请求 查找路由 运行控制器 返回响应结果 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364protected function dispatchToRouter(){ return function ($request) { // 将请求注册到容器 $this->app->instance('request', $request); return $this->router->dispatch($request); };}public function dispatch(Request $request){ $this->currentRequest = $request; return $this->dispatchToRoute($request);}public function dispatchToRoute(Request $request){ return $this->runRoute($request, $this->findRoute($request));}// 查找路由protected function findRoute($request){ $this->current = $route = $this->routes->match($request); $this->container->instance(Route::class, $route); return $route;}protected function runRoute(Request $request, Route $route){ $request->setRouteResolver(function () use ($route) { return $route; }); $this->events->dispatch(new RouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) );}protected function runRouteWithinStack(Route $route, Request $request){ $shouldSkipMiddleware = $this->container->bound('middleware.disable') && $this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); // 返回运行结果 return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { // 运行匹配到的路由控制器或匿名函数 return $this->prepareResponse( $request, $route->run() ); });} 执行 $route->run() 的方法定义在 Illuminate\Routing\Route 类中: 1234567891011121314public function run(){ $this->container = $this->container ?: new Container; try { if ($this->isControllerAction()) { return $this->runController(); } return $this->runCallable(); } catch (HttpResponseException $e) { return $e->getResponse(); }} 如果路由的实现是一个控制器,会完成控制器实例化并执行指定方法;如果是一个匿名函数就会直接调用。最终响应通过 prepareResponse 返回。 3.2.3 发送响应绕了一大圈,最后终于回到了开始的地方 12// 发送响应$response->send(); 最终发送,由 src/Illuminate/Http/Response.php 的父类 Symfony\Component\HttpFoundation\Response 完成: 12345678910111213public function send(){ $this->sendHeaders(); $this->sendContent(); if (\function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); } return $this;} 4 请求结束,进行回调1$kernel->terminate($request, $response); 继续往下看,核心的 terminate 方法: 123456public function terminate($request, $response){ $this->terminateMiddleware($request, $response); $this->app->terminate();} terminateMiddleware 中,进行终止中间件,$this->app->terminate() 终止程序。 总结创建应用实例,完成项目路径注册、基础服务注册、核心类别名注册,然后将 HTTP 和 Console, Exception 核心注册到容器。 然后再实例化内核,将中间件加载到路由,再将请求注册到容器,然后运行引导程序,进行环境检测、加载系统配置等系统环境配置。 然后进行中间件校验,通过校验后才会最终处理实际的控制器或匿名函数并生成响应。 最终,发送响应给用户,清理项目中的中间件,完成一个请求周期。

2018-06-19
Laravel使用chunk自更新有误的原因
Laravel 的 chunk 在查询大量数据时非常好用,但是在用某个字段做为检索条件,并且在程序内部有更新这个字段的时候,就会有一部分数据丢失。 原因是这样的,比如有7条数据,每次取2条,那么这7条数据就会被分为2,2,2,1这样几块,第一次调用时,取得第一页,也就是第一个2的数据块,更新完后再取数据,原来的第二页就变成了第一页,但是这时候取的还是第二页,所以这时候取的是第三个数据块,到了第三次调用的时候,原来的第二页变成了第一页,原来的第四页变成了第二页,第三页就变成了空的,所以第三页就什么都取不到了。 解决方法:可以使用 laravel 的游标 cursor()

2019-03-15
使用 laravel mix 编译资源
学习下如何在 laravel 框架中,用 laravel mix 编译前端资源。 使用本次操作的环境依然是 laradock,如果没用特殊说明,以后应该默认 laradock。 workspace 容器中,已经提前装好了 node 环境,而在项目根目录中,package.json 和 webpack.mix.js 也已经为我们预设好了,所以laravel 项目建好后,直接在根目录安装即可: 1npm install 在 webpack.mix.js 中,已经加载了两个默认的文件: 12mix.js('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css'); 我们只需要把自己的资源文件,按照同样的格式写入进去,然后开始运行,就可以生成编译后的资源了。 虽然示例中只写了 sass 一种样式文件,但是其实可以支持常见的以及不常见的很多中格式,只需要调用对应的接口即可。而且还可以把多个资源文件合并成一个。 举个🌰: 123456mix.less('resources/assets/less/app.less', 'public/stylesheets/styles.css');mix.styles([ 'public/css/vendor/normalize.css', 'public/css/vendor/videojs.css'], 'public/css/all.css'); 运行12npm run devnpm run watch 在上一篇中,我们修改视图后使之生效的命令,其实就是通知 mix 开始工作的。 单独使用在 laravel 框架之外也是可以使用 mix 的,具体教程请参考 [ learnku ] 其实不难,挺简单的。😎

2018-01-05
laravel5.4疑难杂症
公司项目最近翻新了页面,把 bootstrap 完全改成了 layui 。 按照惯例,上线之前先在测试环境跑几天,结果在搭建测试环境的时候,问题就出来了: laravel 版本是 5.4.63 ,服务器的 php 版本是 5.6 ,执行 composer install 时,提示我需要 php7.1 。 吓得我一阵懵逼,难道是什么时候装错扩展了? 把 composer.json 里没什么用的扩展完全去除后再试,结果还是一样。 反复折腾无果,想起还有 update 可以用,遂改为执行 composer update ,终于开始安装了。 小样,还治不了你了!容老夫抽根烟得瑟一下。 下一秒,一个新的报错又砸我个措手不及: class ‘’ not found ! 虽然不知道这个报错是咋回事,但是潜意识觉得应该是某个 Kernel 文件出错了。 找到一份之前的备份,一通对比,终于有所发现: 出错的代码比之前正常的代码多了个 “,”,丫的,太粗糙了! 去掉,再次执行 update ,果然一路畅通无阻。 但是那个该死的 install 是再也没回来。

2017-12-22
laravel 使用笔记
先从最简单的开始: 安装laravel 的安装需要借助 composer ,百度一下,安装,然后去 GitHub 下载 laravel 切换到项目文件夹 ,在不选中任何文件的前提下按住 shift + 鼠标右键,打开 Powershell 或者 cmd 输入:composer install 将 .env.example 另存为 .env 修改数据库配置信息和邮件系统配置信息 然后在命令行输入:php artisan key:generate 生成密钥 然后配置一个本地域名指向 public 文件夹,然后,就好了。[哈哈] 邮件邮件系统配置示例: 12345678MAIL_DRIVER=smtpMAIL_HOST=smtp.mxhichina.comMAIL_PORT=25//465MAIL_USERNAME=m@m-finderMAIL_PASSWORD=邮箱密码MAIL_FROM_NAME=M-finderMAIL_FROM_ADDRESS=m@m-finderMAIL_ENCRYPTION=null//ssl #如果用465端口的话,需要参数 MAIL_ENCRYPTION=ssl 邮件有 3 种模式(可能更多,暂时只接触到3种):一种用 Mail::send 方法一种用 Mail::raw另外一种则是官方文档中的,新建一个类,然后发送邮件时实例化这个类。3 种方法实现的功能一样。Mail::send 1234Mail::send('admin.email', ['orderPrice' => 'laravel'], function ($message) { $message->to('m@m-finder'); $message->subject('我是自定义标题');}); 这个方法第一个参数为视图文件,视图文件的用法等同于普通视图,第二个参数为视图中所用到的数据Mail::raw 1234567$content = '这是一封来自Laravel的测试邮件.';$toMail = 'm@m-finder'; Mail::raw($content, function ($message) use ($toMail) { $message->subject('[ 测试 ] 测试邮件SendMail - ' . date('Y-m-d H:i:s')); $message->to($toMail);}); 基本等同于Mail::send 第三种方法 12345678910php artisan make:controller MailControllerphp artisan make:mail OrderShipped在 OrderShipped 增加内容return $this->view('admin.email')->with([ 'orderName' => 'test', 'orderPrice' => 1500, ]); 然后在要发送邮件的方法中调用: 1Mail::to('m@m-finder')->send(new OrderShipped()); 开放路由,访问下就可以了。 如果需要自定义邮件标题,可以试一下以下方法(未测试): 在你的类中定义一个subject变量: 1public $subject = '这里是邮件自定义标题'; 或者在你的view后跟一个subject方法: 1view('emails.activate-user')->subject('这里定义邮件标题'); 多视图共享数据在 app\Providers 文件夹下 boot() 方法中写入要共享的数据即可 , 例如 : 123456789101112public function boot() { $links = Link::orderBy('id', 'desc')->get(); $web_info = SysConfig::first(); $menus = Menu::select('id', 'name', 'type', 'seo_title', 'seo_describe', 'link') ->where('pid', '=', 0) ->where('is_show', '=', '2') ->get(); view()->share('links', $links); view()->share('web_info', $web_info); view()->share('menus', $menus);} 这样写完以后,你会发现你的 migrate 挂了,哈哈 ,解决办法是使用闭包,即 composer 方法: 12345678910111213public function boot() { Schema::defaultStringLength(191); //解决数据库版本过低无法执行 migrate view()->composer(['layouts.home', 'layouts.userhome','layouts.admin'], function($view) { $links = Link::orderBy('id', 'desc')->get(); $web_info = SysConfig::first(); $menus = Menu::select('id', 'name', 'type', 'seo_title', 'seo_describe', 'link') ->where('pid', '=', 0) ->where('is_show', '=', '2') ->get(); $view->with(['links' => $links, 'web_info' => $web_info, 'menus' => $menus]); }); } 文件上传config 文件夹下有一个 filesystems.php,里边是默认的上传地址,可以根据自己的需要做修改或者添加 上传的控制器代码: 12345678910111213141516if ($file->isValid()) { if ($file->getClientSize() > 2097152) { return $this->json_response(1, "请上传小于 2 mb 的图片", 0); } $ext = $file->getClientOriginalExtension(); $realPath = $file->getRealPath(); $type = $file->getClientMimeType(); $filename = date('Y-m-d-H-i-s') . '-' . uniqid() . '.' . $ext; $bool = Storage::disk($path)->put($filename, file_get_contents($realPath)); $url = Storage::disk($path)->url($filename); if ($filename) { return ['code' => 0, 'msg' => '', 'src' => $url, 'data'=>['src'=>$url,'title'=>$filename]]; //{"code": 0 ,"msg": "" ,"data": {"src": "图片路径","title": "图片名称"} layui 图片上传接口 }} 访问上传到本地的文件资源,需要先创建一个软连接:php artisan storage:link 一个页面中,如果有 ajax 调取数据的,可以把 ajax 使用的路由和页面的路由名称定义为同一个,不同的是页面是 get ,ajax 是 post 自定义404页面在 app\Exceptions文件夹下的Hander中有个render方法,改造一下: 123456789101112public function render($request, Exception $exception){ if ($exception instanceof ModelNotFoundException) { $exception = new NotFoundHttpException($exception->getMessage(), $exception); } if ( ! config('app.debug')) { return response()->view('errors.500', [], 500); } return parent::render($request, $exception);} 然后在views文件夹新建error文件夹和对应错误代码的blade文件。

2019-03-14
laravel 内置 vue 的使用
从 5.3 版本开始,用 Vue.js 作为默认 JavaScript 前端框架。 从刚接触 laravel 到现在已经又过去了四个版本,种种原因,还是一直没能用上 vue.js 来做开发,现在刚好因为公司项目用到了 vue,对 vue 有了一定的了解,所以顺便就研究下 vue 在 laravel 中的使用吧。 安装laravel操作均在 laradock 的环境中进行。进入 workspace 容器,执行以下命令安装 laravel 1composer create-project laravel/laravel study 配置mysqldocker-compose up -d nginx mysql phpmyadmin 启动容器配置 nginx、hosts 并重启 nginx进入 mysql 容器执行以下命令: 123456mysql -uroot -prootALTER USER root IDENTIFIED WITH mysql_native_password BY 'PASSWORD';exit;exit 访问 phpmyadmin: localhost:8080,host 填写 mysql,用户名密码均为 root。 配置laravel修改数据库信息,生成用户模块并安装前端脚手架: 1234567891011121314php artisan make:authphp artisan migratephp artisan make:seed UsersTableSeeder在 run 方法中添加用户信息:$user = new App\User;$user->name = 'wu';$user->email = 'yf-wu@qq.com';$user->password = Hash::make('111111');$user->save();再去 DatabaseSeeder 中打开 run 中的注释,接着往下执行:php artisan db:seednpm install 修改视图home.blade.php:vue 的组件在 resources/js/components,然后在 app.js 中注册。 12You are logged in!<example-component></example-component> 更新脚手架:npm run dev or npm run watch 再实验下例子来自:[ cxp1539 ] 视图组件: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657<template> <transition name="fade"> <div v-if="isShow" class="goTop" @click="goTop"> <span class="glyphicon glyphicon-menu-up"></span> </div> </transition></template><script>export default { data() { return { isShow: false } }, mounted() { const that = this $(window).scroll(function() { if ($(this).scrollTop() > 50) { that.isShow = true } else { that.isShow = false } }) }, methods: { goTop() { $('html,body').animate({ scrollTop: 0 }) } }}</script><style scoped lang="scss"> .fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; } .goTop { position: fixed; right: 36px; bottom: 50px; background: #FFF; width: 50px; height: 50px; line-height: 60px; text-align: center; border-radius: 2px; box-shadow: 0 4px 12px 0 rgba(7,17,27,.1); cursor: pointer; z-index: 1000; span { font-size: 20px; } }</style> app.js 注册: 1Vue.component('go-top', require('./components/GoTop.vue')); 在 app.blade.php 中引入组件: 1234<main class="py-4"> @yield('content')</main><go-top></go-top> 为了使页面更高,随便修改个样式使滚动条出现。 注意事项 每次修改组件后都需要重新运行一次 npm run dev,也可以用 watch-poll 监听。 进阶使用到了上一步已经可以完成一些基础的操作了,实际上,刚才得操作还用到了一个叫做 laravel-mix 的东西,在 [ LearnKu ] (laravel-china 社区)社区的文档中是这么介绍的: Laravel Mix 提供了简洁且可读性高的 API ,用于使用几个常见的 CSS 和 JavaScript 预处理器为应用定义 Webpack 构建步骤。可以通过简单链式调用来定义资源的编译。 Laravel Mix 是叠加于 webpack 上的一层干净的膜, 它让 webpack 百分之80的用例变得十分简单。 也就是说,laravel-mix 是用来简化 webpack 学习和开发成本的工具。 对于后端人员来说,前端东西真的太多太难,mix 可以让我们不需要关注 webpack 的配置,即可轻松的编译前端脚本。 之前因为没在框架中用过 vue,所以一直也没有接触到这个工具,现在看完发现,学习之路真的是永无止境… 😂