In magento 2,they use a jquery plugin Plugin Link to manage uploading files to the tmp foler and saving them to the database.I have uses the option in the plugin which gives me multiple files in the same xhr request.Now how do i actually zip the files before saving them to the database.Can anybody give me a clear working example of the same.
Here is the code snippets of the required files.
file-uploader.js(app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js)
define([
'jquery',
'underscore',
'mageUtils',
'Magento_Ui/js/modal/alert',
'Magento_Ui/js/lib/validation/validator',
'Magento_Ui/js/form/element/abstract',
'jquery/file-uploader'
], function ($, _, utils, uiAlert, validator, Element) {
'use strict';
debugger;
return Element.extend({
defaults: {
value: [],
maxFileSize: false,
isMultipleFiles: false,
placeholderType: 'document', // 'image', 'video'
allowedExtensions: false,
previewTmpl: 'ui/form/element/uploader/preview',
dropZone: '[data-role=drop-zone]',
isLoading: false,
uploaderConfig: {
dataType: 'json',
sequentialUploads:false,
singleFileUploads:false,
formData: {
'form_key': window.FORM_KEY
}
},
tracks: {
isLoading: true
}
},
/**
* Initializes file uploader plugin on provided input element.
*
* @param {HTMLInputElement} fileInput
* @returns {FileUploader} Chainable.
*/
initUploader: function (fileInput) {
this.$fileInput = fileInput;
_.extend(this.uploaderConfig, {
dropZone: $(fileInput).closest(this.dropZone),
change: this.onFilesChoosed.bind(this),
drop: this.onFilesChoosed.bind(this),
add: this.onBeforeFileUpload.bind(this),
done: this.onFileUploaded.bind(this),
start: this.onLoadingStart.bind(this),
stop: this.onLoadingStop.bind(this)
});
$(fileInput).fileupload(this.uploaderConfig);
return this;
},
/**
* Defines initial value of the instance.
*
* @returns {FileUploader} Chainable.
*/
setInitialValue: function () {
var value = this.getInitialValue();
value = value.map(this.processFile, this);
this.initialValue = value.slice();
this.value(value);
this.on('value', this.onUpdate.bind(this));
this.isUseDefault(this.disabled());
return this;
},
/**
* Empties files list.
*
* @returns {FileUploader} Chainable.
*/
clear: function () {
this.value.removeAll();
return this;
},
/**
* Checks if files list contains any items.
*
* @returns {Boolean}
*/
hasData: function () {
return !!this.value().length;
},
/**
* Resets files list to its' initial value.
*
* @returns {FileUploader}
*/
reset: function () {
var value = this.initialValue.slice();
this.value(value);
return this;
},
/**
* Adds provided file to the files list.
*
* @param {Object} file
* @returns {FileUploder} Chainable.
*/
addFile: function (file) {
file = this.processFile(file);
this.isMultipleFiles ?
this.value.push(file) :
this.value([file]);
return this;
},
/**
* Retrieves from the list file which matches
* search criteria implemented in itertor function.
*
* @param {Function} fn - Function that will be invoked
* for each file in the list.
* @returns {Object}
*/
getFile: function (fn) {
return _.find(this.value(), fn);
},
/**
* Removes provided file from thes files list.
*
* @param {Object} file
* @returns {FileUploader} Chainable.
*/
removeFile: function (file) {
this.value.remove(file);
return this;
},
/**
* May perform modifications on the provided
* file object before adding it to the files list.
*
* @param {Object} file
* @returns {Object} Modified file object.
*/
processFile: function (file) {
file.previewType = this.getFilePreviewType(file);
this.observe.call(file, true, [
'previewWidth',
'previewHeight'
]);
return file;
},
/**
* Formats incoming bytes value to a readable format.
*
* @param {Number} bytes
* @returns {String}
*/
formatSize: function (bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'],
i;
if (bytes === 0) {
return '0 Byte';
}
i = window.parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
},
/**
* Returns path to the files' preview image.
*
* @param {Object} file
* @returns {String}
*/
getFilePreview: function (file) {
return file.url;
},
/**
* Returns path to the file's preview template.
*
* @returns {String}
*/
getPreviewTmpl: function () {
return this.previewTmpl;
},
/**
* Checks if provided file is allowed to be uploaded.
*
* @param {Object} file
* @returns {Object} Validation result.
*/
isFileAllowed: function (file) {
var result;
_.every([
this.isExtensionAllowed(file),
this.isSizeExceeded(file)
], function (value) {
result = value;
return value.passed;
});
return result;
},
/**
* Checks if extension of provided file is allowed.
*
* @param {Object} file - File to be checked.
* @returns {Boolean}
*/
isExtensionAllowed: function (file) {
return validator('validate-file-type', file.name, this.allowedExtensions);
},
/**
* Get simplified file type.
*
* @param {Object} file - File to be checked.
* @returns {String}
*/
getFilePreviewType: function (file) {
var type;
if (!file.type) {
return 'document';
}
type = file.type.split('/')[0];
return type !== 'image' && type !== 'video' ? 'document' : type;
},
/**
* Checks if size of provided file exceeds
* defined in configuration size limits.
*
* @param {Object} file - File to be checked.
* @returns {Boolean}
*/
isSizeExceeded: function (file) {
return validator('validate-max-size', file.size, this.maxFileSize);
},
/**
* Displays provided error message.
*
* @param {String} msg
* @returns {FileUploader} Chainable.
*/
notifyError: function (msg) {
uiAlert({
content: msg
});
return this;
},
/**
* Performs data type conversions.
*
* @param {*} value
* @returns {Array}
*/
normalizeData: function (value) {
return utils.isEmpty(value) ? [] : value;
},
/**
* Checks if files list is different
* from its' initial value.
*
* @returns {Boolean}
*/
hasChanged: function () {
var value = this.value(),
initial = this.initialValue;
return !utils.equalArrays(value, initial);
},
/**
* Abstract handler which is invoked when files are choosed for upload.
* May be used for implementation of aditional validation rules,
* e.g. total files and a total size rules.
*
* @abstract
*/
onFilesChoosed: function () {},
/**
* Handler which is invoked prior to the start of a file upload.
*
* @param {Event} e - Event object.
* @param {Object} data - File data that will be uploaded.
*/
onBeforeFileUpload: function (e, data) {
debugger;
// var sample = new JSZip();
// // sample.file("test.zip",data.files)
// console.log(data.files.length)
// for(var i=0;i<data.files.length;i++)
// {
// }
var file = data.files[0],
allowed = this.isFileAllowed(file),
target = $(e.target);
if (allowed.passed) {
target.on('fileuploadsend', function (event, postData) {
postData.data.append('param_name', this.paramName);
}.bind(data));
target.fileupload('process', data).done(function () {
data.submit();
});
} else {
this.notifyError(allowed.message);
}
},
/**
* Handler of the file upload complete event.
*
* @param {Event} e
* @param {Object} data
*/
onFileUploaded: function (e, data) {
debugger;
console.log(data)
var file = data.result,
error = file.error;
error ?
this.notifyError(error) :
this.addFile(file);
},
/**
* Load start event handler.
*/
onLoadingStart: function () {
this.isLoading = true;
},
/**
* Load stop event handler.
*/
onLoadingStop: function () {
this.isLoading = false;
},
/**
* Handler function which is supposed to be invoked when
* file input element has been rendered.
*
* @param {HTMLInputElement} fileInput
*/
onElementRender: function (fileInput) {
this.initUploader(fileInput);
},
/**
* Handler of the preview image load event.
*
* @param {Object} file - File associated with an image.
* @param {Event} e
*/
onPreviewLoad: function (file, e) {
var img = e.currentTarget;
file.previewWidth = img.naturalWidth;
file.previewHeight = img.naturalHeight;
},
/**
* Restore value to default
*/
restoreToDefault: function () {
var defaultValue = utils.copy(this.default);
defaultValue.map(this.processFile, this);
this.value(defaultValue);
},
/**
* Update whether value differs from default value
*/
setDifferedFromDefault: function () {
var value = utils.copy(this.value());
this.isDifferedFromDefault(!_.isEqual(value, this.default));
}
});
});
Upload.php(app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php)
<?php
namespace Magento\Downloadable\Controller\Adminhtml\Downloadable\File;
use Magento\Framework\Controller\ResultFactory;
class Upload extends \Magento\Downloadable\Controller\Adminhtml\Downloadable\File
{
/**
* @var \Magento\Downloadable\Model\Link
*/
protected $_link;
/**
* @var \Magento\Downloadable\Model\Sample
*/
protected $_sample;
/**
* Downloadable file helper.
*
* @var \Magento\Downloadable\Helper\File
*/
protected $_fileHelper;
/**
* @var \Magento\MediaStorage\Model\File\UploaderFactory
*/
private $uploaderFactory;
/**
* @var \Magento\MediaStorage\Helper\File\Storage\Database
*/
private $storageDatabase;
/**
*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Downloadable\Model\Link $link
* @param \Magento\Downloadable\Model\Sample $sample
* @param \Magento\Downloadable\Helper\File $fileHelper
* @param \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory
* @param \Magento\MediaStorage\Helper\File\Storage\Database $storageDatabase
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Downloadable\Model\Link $link,
\Magento\Downloadable\Model\Sample $sample,
\Magento\Downloadable\Helper\File $fileHelper,
\Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
\Magento\MediaStorage\Helper\File\Storage\Database $storageDatabase
) {
parent::__construct($context);
$this->_link = $link;
$this->_sample = $sample;
$this->_fileHelper = $fileHelper;
$this->uploaderFactory = $uploaderFactory;
$this->storageDatabase = $storageDatabase;
}
/**
* Upload file controller action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
$type = $this->getRequest()->getParam('type');
$tmpPath = '';
if ($type == 'samples') {
$tmpPath = $this->_sample->getBaseTmpPath();
} elseif ($type == 'links') {
$tmpPath = $this->_link->getBaseTmpPath();
} elseif ($type == 'link_samples') {
$tmpPath = $this->_link->getBaseSampleTmpPath();
}
try {
$uploader = $this->uploaderFactory->create(['fileId' => $type]);
// $uploader2 = $this->uploaderFactory->create(['fileId' => $type]);
print_r($uploader->getUploadedFileName());
// print_r($uploader2->getUploadedFileName());
// print_r('Type !!!!!!!');
// print_r(gettype($uploader));
// echo "\n";
// print_r(array_keys((array)$uploader));
//,'enctype'=>'multipart/form-data'
$keys_of_uploader=array_keys((array)$uploader);
// print_r($keys_of_uploader);
// print_r(gettype($keys_of_uploader));
// print($uploader[5][0]);
// print_r($_POST["param_name"]);
// foreach ($keys_of_uploader as $key1) {
// echo "\n";
// print_r($key1);
// echo "\n";
// // print_r($uploader[$key1]);
// }
// print_r(get_class_methods($uploader));
// print_r($uploader->getUploadedFileName());
// foreach ($_POST as $key1 =>$value1) {
// echo "<tr>";
// echo "<td>";
// echo $key1;
// echo "</td>";
// echo "<td>";
// echo $value1;
// echo "</td>";
// echo "</tr>";
// }
$result = $this->_fileHelper->uploadFromTmp($tmpPath, $uploader);
// print_r('############################# Result of Uplaoding a File');
// print_r($result);
// print_r('!!!!!!!!!!!!!!!!!! After Uploading and saving is done');
if (!$result) {
throw new \Exception('File can not be moved from temporary folder to the destination folder.');
}
unset($result['tmp_name'], $result['path']);
if (isset($result['file'])) {
$relativePath = rtrim($tmpPath, '/') . '/' . ltrim($result['file'], '/');
$this->storageDatabase->saveFile($relativePath);
}
$result['cookie'] = [
'name' => $this->_getSession()->getName(),
'value' => $this->_getSession()->getSessionId(),
'lifetime' => $this->_getSession()->getCookieLifetime(),
'path' => $this->_getSession()->getCookiePath(),
'domain' => $this->_getSession()->getCookieDomain(),
];
} catch (\Exception $e) {
$result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()];
}
return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setData($result);
}
}
As far as i have understood the flow of uploading a file,as soon as the user clicks the add link button for a downloadable product,the file-uploader.js file is loaded and it initializes the jquery plugin for file upload with the given settings.Then as soon as the user selects the files needed for upload,the onBeforeFile upload function is called and then that function calls the Upload.php and it executes it's functionality.