├── .gitattributes
├── .gitignore
├── .travis.yml
├── README.md
├── composer.json
├── config
└── plupload.php
├── phpunit.xml
├── resources
├── assets
│ └── js
│ │ └── upload.js
├── lang
│ └── en
│ │ └── ui.php
└── views
│ └── uploader.blade.php
├── src
├── Contracts
│ └── Plupload.php
├── Exception.php
├── Facades
│ └── Plupload.php
├── File.php
├── Html.php
├── Plupload.php
├── PluploadServiceProvider.php
└── helpers.php
└── tests
└── .gitkeep
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - hhvm
8 |
9 | before_script:
10 | - travis_retry composer self-update
11 | - travis_retry composer install --prefer-source --no-interaction --dev
12 |
13 | script: phpunit
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Laravel 5 Plupload
3 |
4 | [](https://packagist.org/packages/jenky/laravel-plupload)
5 | [](https://packagist.org/packages/jenky/laravel-plupload)
6 | [](https://packagist.org/packages/jenky/laravel-plupload)
7 |
8 | ##### Laravel package for Plupload http://plupload.com.
9 | This package uses some parts of https://github.com/jildertmiedema/laravel-plupload
10 |
11 | ## Installation
12 | Require this package with composer:
13 |
14 | ```
15 | composer require jenky/laravel-plupload
16 | ```
17 |
18 | Laravel 5.5+ uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider.
19 |
20 | **For Laravel 5.4 or older**
21 |
22 | Add the ServiceProvider to the providers array in `config/app.php`
23 |
24 | ```php
25 | Jenky\LaravelPlupload\PluploadServiceProvider::class,
26 | ```
27 |
28 | and add this to your facades in `config/app.php`:
29 |
30 | ```php
31 | 'Plupload' => Jenky\LaravelPlupload\Facades\Plupload::class,
32 | ```
33 |
34 | Copy the package config to your local config with the publish command:
35 |
36 | ```
37 | php artisan vendor:publish
38 | ```
39 | or
40 | ```
41 | php artisan vendor:publish --provider="Jenky\LaravelPlupload\PluploadServiceProvider"
42 | ```
43 |
44 |
45 | ## Usage
46 |
47 |
48 | ### Uploading files
49 | ##### 1. Use default plupload html
50 |
51 | Use the [examples](http://www.plupload.com/examples) found on the plupload site. The [Getting Started](http://plupload.com/docs/Getting-Started) page is good place to start.
52 |
53 |
54 | ##### 2. Plupload builder
55 |
56 | **make($id, $url)**
57 |
58 | Create new uploader.
59 | * **$id**: the unique identification for the uploader.
60 | * **$url**: the upload url end point.
61 | ```php
62 | {!! Plupload::make('my_uploader_id', route('photos.store'))->render() !!}
63 | ```
64 | or use the helper
65 | ```php
66 | {!! plupload()->make('my_uploader_id', route('photos.store')) !!}
67 | // or even shorter
68 | {!! plupload('my_uploader_id', route('photos.store')) !!}
69 | ```
70 |
71 | **render($view = 'plupload::uploader', array $data = [])**
72 |
73 | Renders the uploader. You can customize this by passing a view name and it's data. From version `2.0`, you can omit the `render` method in the builder if you don't want to set the view or extra data.
74 |
75 | ##### 3. Use package js file to initialize Plupload (Optional)
76 |
77 | If you do not want to write your own js to initialize Plupload, you can use the `upload.js` file that included with the package in `resources/views/vendor/plupload/assets/js`. Make sure that you already have `jQuery` loaded on your page.
78 |
79 | **Initialize Plupload**
80 |
81 | ```js
82 |
87 | ```
88 |
89 |
90 | These following methods are useable with the `upload.js` file.
91 |
92 | **Set Uploader options**
93 |
94 | **setOptions(array $options)**
95 |
96 | Set uploader options. Please visit https://github.com/moxiecode/plupload/wiki/Options to see all the options. You can set the default global options in `config/plupload.php`
97 |
98 | ```php
99 | {!! plupload('my_uploader_id', route('photos.store'))
100 | ->setOptions([
101 | 'filters' => [
102 | 'max_file_size' => '2mb',
103 | 'mime_types' => [
104 | ['title' => 'Image files', 'extensions' => 'jpg,gif,png'],
105 | ],
106 | ],
107 | ]) !!}
108 | ```
109 |
110 | **Automatically start upload when files added**
111 |
112 | Use `setAutoStart()` in your builder before calling render() function.
113 |
114 | **setAutoStart($bool)**
115 |
116 | * **$bool**: `true` or `false`
117 |
118 | ```php
119 | {!! plupload('my_uploader_id', route('photos.store'))->setAutoStart(true) !!}
120 | ```
121 |
122 |
123 | ### Receiving files
124 |
125 |
126 | **file($name, $handler)**
127 | * **$name**: the input name.
128 | * **$handler**: callback handler.
129 |
130 | Use this in your route or your controller. Feel free to modify to suit your needs.
131 |
132 | ```php
133 | return Plupload::file('file', function($file) {
134 | // Store the uploaded file using storage disk
135 | $path = Storage::disk('local')->putFile('photos', $file);
136 |
137 | // Save the record to the db
138 | $photo = App\Photo::create([
139 | 'name' => $file->getClientOriginalName(),
140 | 'type' => 'image',
141 | // ...
142 | ]);
143 |
144 | // This will be included in JSON response result
145 | return [
146 | 'success' => true,
147 | 'message' => 'Upload successful.',
148 | 'id' => $photo->id,
149 | // 'url' => $photo->getImageUrl($filename, 'medium'),
150 | // 'deleteUrl' => route('photos.destroy', $photo)
151 | // ...
152 | ];
153 | });
154 | ```
155 | Helper is also available
156 | ```php
157 | return plupload()->file('file', function($file) {
158 |
159 | });
160 | ```
161 |
162 |
163 | If you are using the package `upload.js` file. The `url` and `deleteUrl` in the JSON payload will be used to generate preview and delete link while the `id` will be appended to the uploader as a hidden field with the following format:
164 |
165 | ``.
166 |
167 | Please note that the `deleteUrl` uses `DELETE` method.
168 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jenky/laravel-plupload",
3 | "description": "Plupload package for Laravel 5",
4 | "keywords": [
5 | "laravel",
6 | "plupload"
7 | ],
8 | "license": "MIT",
9 | "authors": [
10 | {
11 | "name": "Linh Tran",
12 | "email": "jenky.w0w@gmail.com"
13 | }
14 | ],
15 | "require": {
16 | "php": ">=5.5.9",
17 | "illuminate/contracts": "^5.2",
18 | "illuminate/support": "^5.2"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "Jenky\\LaravelPlupload\\": "src/"
23 | },
24 | "files": [
25 | "src/helpers.php"
26 | ]
27 | },
28 | "extra": {
29 | "branch-alias": {
30 | "dev-master": "2.0-dev",
31 | "dev-develop": "3.0-dev"
32 | },
33 | "laravel": {
34 | "providers": [
35 | "Jenky\\LaravelPlupload\\PluploadServiceProvider"
36 | ],
37 | "aliases": {
38 | "Plupload": "Jenky\\LaravelPlupload\\Facades\\Plupload"
39 | }
40 | }
41 | },
42 | "config": {
43 | "sort-packages": true
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/config/plupload.php:
--------------------------------------------------------------------------------
1 | storage_path('plupload'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Plupload Global Options
21 | |--------------------------------------------------------------------------
22 | |
23 | | Set default global options for Plupload.
24 | |
25 | | See https://github.com/moxiecode/plupload/wiki/Options
26 | |
27 | */
28 |
29 | 'global' => [
30 | 'flash_swf_url' => '/js/Moxie.swf',
31 | 'silverlight_xap_url' => '/js/Moxie.xap',
32 | ],
33 |
34 | ];
35 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/resources/assets/js/upload.js:
--------------------------------------------------------------------------------
1 | function createUploader(uploaderId)
2 | {
3 | var $uploader = $('#uploader-' + uploaderId);
4 |
5 | if (!$uploader || !$uploader.length) {
6 | alert('Cannot find uploader');
7 | }
8 |
9 | var $filelist = $uploader.find('.filelist'),
10 | $uploaded = $uploader.find('.uploaded'),
11 | $uploadAction = $uploader.find('.upload-actions'),
12 | $uploadBtn = $('#' + $uploader.data('uploadbtn')) || false,
13 | options = $uploader.data('options') || {},
14 | autoStart = $uploader.data('autostart') || false,
15 | deleteUrl = $uploader.data('deleteurl') || false,
16 | deleteMethod = $uploader.data('deletemethod') || 'DELETE';
17 |
18 | defaultOptions = {
19 | init: {
20 | PostInit: function(up) {
21 | if (!autoStart && $uploadBtn) {
22 | $uploadBtn.click(function() {
23 | up.start();
24 | return false;
25 | });
26 | }
27 | },
28 |
29 | FilesAdded: function(up, files) {
30 | $.each(files, function(i, file){
31 | $filelist.append(
32 | '
' +
33 | '
' + file.name + ' (' + plupload.formatSize(file.size) + ')
' +
34 | '
');
35 |
36 | $filelist.on('click', '#' + file.id + ' button.cancelUpload', function() {
37 | var $this = $(this),
38 | $file = $('#' + file.id),
39 | deleteUrl = $this.data('deleteurl') || false,
40 | id = $this.data('id') || false;
41 |
42 | if (deleteUrl) {
43 | $.ajax({
44 | dataType: 'json',
45 | type: deleteMethod,
46 | url: deleteUrl,
47 | data: options.multipart_params,
48 | success: function(result) {
49 | if (result.success) {
50 | up.removeFile(file);
51 | $file.remove();
52 | $('#' + file.id + '-hidden').remove();
53 | $uploadAction.show();
54 | }
55 | else {
56 | $('#' + file.id).append('' + result.message + '');
57 | }
58 | }
59 | });
60 | }
61 | else {
62 | $uploadAction.show();
63 | $file.remove();
64 | $('#' + file.id + '-hidden').remove();
65 | up.removeFile(file);
66 | }
67 | });
68 | });
69 | up.refresh(); // Reposition Flash/Silverlight
70 | if (autoStart) {
71 | $uploadAction.hide();
72 | up.start();
73 | }
74 | },
75 |
76 | UploadProgress: function(up, file) {
77 | $uploadAction.hide();
78 | $('#' + file.id + ' .progress').addClass('active');
79 | $('#' + file.id + ' button.cancelUpload').hide();
80 | $('#' + file.id + ' .progress .progress-bar').animate({width: file.percent + '%'}, 100, 'linear');
81 | },
82 |
83 | Error: function(up, err) {
84 | $filelist.append('' +
85 | 'Error: ' + err.code + ', Message: ' + err.message +
86 | (err.file ? ', File: ' + err.file.name : '') +
87 | "
"
88 | );
89 | up.refresh(); // Reposition Flash/Silverlight
90 | },
91 |
92 | FileUploaded: function(up, file, info) {
93 | var response = JSON.parse(info.response);
94 |
95 | $('#' + file.id + ' .progress .progress-bar').animate({width: '100%'}, 100, 'linear');
96 | $('#' + file.id + ' .progress').removeClass('progress-striped').removeClass('active').fadeOut();
97 | $('#' + file.id + ' .filename').removeClass('hide').show();
98 | $('#' + file.id + ' button.cancelUpload').show();
99 |
100 | if (response.result.id) {
101 | $('#' + file.id + ' button.cancelUpload').attr('data-id', response.result.id);
102 | $('').appendTo($uploader);
103 | }
104 |
105 | if (response.result.deleteUrl) {
106 | $('#' + file.id + ' button.cancelUpload').attr('data-deleteurl', response.result.deleteUrl);
107 | }
108 |
109 | if (response.result.url) {
110 | $('#' + file.id).append('
');
111 | }
112 |
113 | }
114 | }
115 | };
116 |
117 | $.extend(options, defaultOptions);
118 |
119 | var uploader = new plupload.Uploader(options);
120 | uploader.init();
121 | }
122 |
--------------------------------------------------------------------------------
/resources/lang/en/ui.php:
--------------------------------------------------------------------------------
1 | 'Browse',
16 | 'upload' => 'Upload',
17 |
18 | ];
19 |
--------------------------------------------------------------------------------
/resources/views/uploader.blade.php:
--------------------------------------------------------------------------------
1 | @if (!empty($options['url']))
2 |
21 | @else
22 | Missing URL option.
23 | @endif
24 |
--------------------------------------------------------------------------------
/src/Contracts/Plupload.php:
--------------------------------------------------------------------------------
1 | request = $request;
37 | $this->storage = $file;
38 | }
39 |
40 | /**
41 | * Get chuck upload path.
42 | *
43 | * @return string
44 | */
45 | public function getChunkPath()
46 | {
47 | $path = config('plupload.chunk_path');
48 |
49 | if (! $this->storage->isDirectory($path)) {
50 | $this->storage->makeDirectory($path, 0777, true);
51 | }
52 |
53 | return $path;
54 | }
55 |
56 | /**
57 | * Process uploaded files.
58 | *
59 | * @param string $name
60 | * @param \Closure $closure
61 | * @return array
62 | */
63 | public function process($name, Closure $closure)
64 | {
65 | $response = [];
66 | $response['jsonrpc'] = '2.0';
67 |
68 | if ($this->hasChunks()) {
69 | $result = $this->chunks($name, $closure);
70 | } else {
71 | $result = $this->single($name, $closure);
72 | }
73 |
74 | $response['result'] = $result;
75 |
76 | return $response;
77 | }
78 |
79 | /**
80 | * Handle single uploaded file.
81 | *
82 | * @param string $name
83 | * @param \Closure $closure
84 | * @return void
85 | */
86 | public function single($name, Closure $closure)
87 | {
88 | if ($this->request->hasFile($name)) {
89 | return $closure($this->request->file($name));
90 | }
91 | }
92 |
93 | /**
94 | * Handle single uploaded file.
95 | *
96 | * @param string $name
97 | * @param \Closure $closure
98 | * @return mixed
99 | */
100 | public function chunks($name, Closure $closure)
101 | {
102 | if (! $this->request->hasFile($name)) {
103 | return;
104 | }
105 |
106 | $file = $this->request->file($name);
107 | $chunk = (int) $this->request->input('chunk', false);
108 | $chunks = (int) $this->request->input('chunks', false);
109 | $originalName = $this->request->input('name');
110 |
111 | $filePath = $this->getChunkPath().'/'.$originalName.'.part';
112 |
113 | $this->removeOldData($filePath);
114 | $this->appendData($filePath, $file);
115 |
116 | if ($chunk == $chunks - 1) {
117 | $file = new UploadedFile($filePath, $originalName, 'blob', UPLOAD_ERR_OK, true);
118 | @unlink($filePath);
119 |
120 | return $closure($file);
121 | }
122 | }
123 |
124 | /**
125 | * Remove old chunks.
126 | *
127 | * @param string $filePath
128 | * @return void
129 | */
130 | protected function removeOldData($filePath)
131 | {
132 | if ($this->storage->exists($filePath) && ($this->storage->lastModified($filePath) < time() - $this->maxFileAge)) {
133 | $this->storage->delete($filePath);
134 | }
135 | }
136 |
137 | /**
138 | * Merge chunks.
139 | *
140 | * @param string $filePathPartial
141 | * @param \Illuminate\Http\UploadedFile $file
142 | * @return void
143 | */
144 | protected function appendData($filePathPartial, UploadedFile $file)
145 | {
146 | if (! $out = @fopen($filePathPartial, 'ab')) {
147 | throw new Exception('Failed to open output stream.', 102);
148 | }
149 |
150 | if (! $in = @fopen($file->getPathname(), 'rb')) {
151 | throw new Exception('Failed to open input stream', 101);
152 | }
153 |
154 | while ($buff = fread($in, 4096)) {
155 | fwrite($out, $buff);
156 | }
157 |
158 | @fclose($out);
159 | @fclose($in);
160 | }
161 |
162 | /**
163 | * Check if request has chunks.
164 | *
165 | * @return bool
166 | */
167 | public function hasChunks()
168 | {
169 | return (bool) $this->request->input('chunks', false);
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/Html.php:
--------------------------------------------------------------------------------
1 | id = $id;
37 | $this->options = config('plupload.global', []);
38 | $this->options['url'] = $url;
39 | }
40 |
41 | /**
42 | * Initialize the options.
43 | *
44 | * @return void
45 | */
46 | protected function init()
47 | {
48 | if (empty($this->options['url'])) {
49 | throw new Exception('Missing URL option.');
50 | }
51 |
52 | $options = $this->options;
53 | $id = $this->id;
54 | $autoStart = $this->autoStart;
55 |
56 | // csrf token
57 | $options['multipart_params']['_token'] = csrf_token();
58 | $options['browse_button'] = 'uploader-'.$this->id.'-pickfiles';
59 | $options['container'] = 'uploader-'.$this->id.'-container';
60 |
61 | $this->data = array_merge($this->data, compact('options', 'id', 'autoStart'));
62 | }
63 |
64 | /**
65 | * Set uploader auto start.
66 | *
67 | * @param bool $bool
68 | * @return void
69 | */
70 | public function setAutoStart($bool)
71 | {
72 | $this->autoStart = (bool) $bool;
73 |
74 | return $this;
75 | }
76 |
77 | /**
78 | * Set uploader options.
79 | *
80 | * @see https://github.com/moxiecode/plupload/wiki/Options
81 | * @param array $options
82 | * @return $this
83 | */
84 | public function setOptions(array $options)
85 | {
86 | $this->options = array_merge($this->options, array_except($options, ['url']));
87 |
88 | return $this;
89 | }
90 |
91 | /**
92 | * Set uploader custom params.
93 | *
94 | * @param array $params
95 | * @return void
96 | */
97 | public function setCustomParams(array $params)
98 | {
99 | $this->data['params'] = $params;
100 |
101 | return $this;
102 | }
103 |
104 | /**
105 | * Render the upload handler buttons.
106 | *
107 | * @param string $view
108 | * @param array $data
109 | * @return \Illuminate\View\View
110 | */
111 | public function render($view = 'plupload::uploader', array $data = [])
112 | {
113 | $this->init();
114 |
115 | return view($view, array_merge($this->data, $data));
116 | }
117 |
118 | /**
119 | * Get the string contents of the view.
120 | *
121 | * @return string
122 | */
123 | public function __toString()
124 | {
125 | return (string) $this->render();
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/Plupload.php:
--------------------------------------------------------------------------------
1 | app = $app;
25 | }
26 |
27 | /**
28 | * File upload handler.
29 | *
30 | * @param string $name
31 | * @param closure $closure
32 | * @return void
33 | */
34 | public function file($name, Closure $closure)
35 | {
36 | $fileHandler = $this->app->make(File::class);
37 |
38 | return $fileHandler->process($name, $closure);
39 | }
40 |
41 | /**
42 | * Html template handler.
43 | *
44 | * @param string $id
45 | * @param string $url
46 | * @return \Jenky\LaravelPlupload\Html
47 | */
48 | public function make($id, $url)
49 | {
50 | return $this->app->make(Html::class, compact('id', 'url'));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/PluploadServiceProvider.php:
--------------------------------------------------------------------------------
1 | registerPlupload();
25 | }
26 |
27 | /**
28 | * Bootstrap the application events.
29 | *
30 | * @return void
31 | */
32 | public function boot()
33 | {
34 | $this->setupConfig();
35 | }
36 |
37 | /**
38 | * Setup the config.
39 | *
40 | * @return void
41 | */
42 | protected function setupConfig()
43 | {
44 | $configPath = __DIR__.'/../config/plupload.php';
45 | $viewsPath = __DIR__.'/../resources/views';
46 | $assetsPath = __DIR__.'/../resources/assets';
47 | $translationsPath = __DIR__.'/../resources/lang';
48 |
49 | $this->mergeConfigFrom($configPath, 'plupload');
50 | $this->loadViewsFrom($viewsPath, 'plupload');
51 | $this->loadTranslationsFrom($translationsPath, 'plupload');
52 |
53 | $this->publishes([$configPath => config_path('plupload.php')], 'config');
54 | $this->publishes([
55 | $viewsPath => base_path('resources/views/vendor/plupload'),
56 | $assetsPath.'/js' => base_path('resources/assets/plupload'),
57 | $translationsPath => base_path('resources/lang/vendor/plupload'),
58 | ]);
59 | }
60 |
61 | /**
62 | * Register the plupload class.
63 | *
64 | * @return void
65 | */
66 | protected function registerPlupload()
67 | {
68 | $this->app->singleton(PluploadContract::class, function ($app) {
69 | return new Plupload($app);
70 | });
71 |
72 | $this->app->alias(PluploadContract::class, 'plupload');
73 | }
74 |
75 | /**
76 | * Get the services provided by the provider.
77 | *
78 | * @return array
79 | */
80 | public function provides()
81 | {
82 | return [PluploadContract::class, 'plupload'];
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/helpers.php:
--------------------------------------------------------------------------------
1 | make($id, $url);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenky/laravel-plupload/19123d6ede6204fea6efdc69286435674c0e3fee/tests/.gitkeep
--------------------------------------------------------------------------------