chunk upload and multipart upload beta.1

This commit is contained in:
carodej
2020-07-29 12:29:30 +02:00
parent bf8db1be52
commit 18761eb5b3
18 changed files with 307 additions and 243 deletions

View File

@@ -16,6 +16,7 @@ But, it can't be done without you, development is more and more complicated and
- [Server Requirements](#server-requirements) - [Server Requirements](#server-requirements)
- [Installation](#installation) - [Installation](#installation)
- [PHP Configuration](#php-configuration) - [PHP Configuration](#php-configuration)
- [Chunk Upload](#chunk-upload)
- [Nginx Configuration](#nginx-configuration) - [Nginx Configuration](#nginx-configuration)
- [Apache Configuration](#apache-configuration) - [Apache Configuration](#apache-configuration)
- [Recover Failed Installation](#installation-failed) - [Recover Failed Installation](#installation-failed)
@@ -82,11 +83,16 @@ That was the hardest part of installation proces. Please follow instructions in
There are several PHP settings good to know to setup before you try upload any file. Please set these values in your php.ini, we provide minimal setup for you. When you set `-1` then you set infinity limits. There are several PHP settings good to know to setup before you try upload any file. Please set these values in your php.ini, we provide minimal setup for you. When you set `-1` then you set infinity limits.
``` ```
memory_limit = 512M memory_limit = 128M
upload_max_filesize = 1024M upload_max_filesize = 128
post_max_size = 1024M post_max_size = 128M
max_file_uploads = 50 max_file_uploads = 50
max_execution_time = 1800
``` ```
## Chunk Upload
VueFileManager in default supporting chunk upload. Default chunk upload size is `128MB`. If you wish change this default value, go to `/config/vuefilemanager.php` and change `chunk_size` attribute.
if you use external storage, and upload large files, to prevent failing upload process make sure you have enough space in your application space.
## Nginx Configuration ## Nginx Configuration
If you running VueFileManager undex Nginx, don't forget set this value in your `nginx.conf` file: If you running VueFileManager undex Nginx, don't forget set this value in your `nginx.conf` file:

View File

@@ -148,6 +148,7 @@ class BrowseController extends Controller
$folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected']) $folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected'])
->where('user_id', $user_id) ->where('user_id', $user_id)
->where('parent_id', $unique_id) ->where('parent_id', $unique_id)
->orderBy('created_at', 'DESC')
->get(); ->get();
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected']) $files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected'])

View File

@@ -589,8 +589,8 @@ class SetupWizardController extends Controller
// Create legal pages and index content // Create legal pages and index content
if ($request->license === 'Extended') { if ($request->license === 'Extended') {
$pages = collect(config('vuefilemanager.pages')); $pages = collect(config('content.pages'));
$content = collect(config('vuefilemanager.content')); $content = collect(config('content.content'));
$content->each(function ($content) { $content->each(function ($content) {
Setting::updateOrCreate($content); Setting::updateOrCreate($content);

View File

@@ -25,8 +25,8 @@ class UpgradeAppController extends Controller
// Create legal pages and index content // Create legal pages and index content
if ($request->license === 'Extended') { if ($request->license === 'Extended') {
$pages = collect(config('vuefilemanager.pages')); $pages = collect(config('content.pages'));
$content = collect(config('vuefilemanager.content')); $content = collect(config('content.content'));
$content->each(function ($content) { $content->each(function ($content) {
Setting::updateOrCreate($content); Setting::updateOrCreate($content);

View File

@@ -463,10 +463,10 @@ function format_date($date, $format = '%d. %B. %Y, %H:%M')
* @param $file * @param $file
* @return string * @return string
*/ */
function get_file_type($file_path) function get_file_type($file_mimetype)
{ {
// Get mimetype from file // Get mimetype from file
$mimetype = explode('/', Storage::disk('local')->mimeType($file_path)); $mimetype = explode('/', $file_mimetype);
switch ($mimetype[0]) { switch ($mimetype[0]) {
case 'image': case 'image':
@@ -483,7 +483,13 @@ function get_file_type($file_path)
} }
} }
// Get mimetype from file
/**
* Get file type from mimetype
*
* @param $mimetype
* @return mixed
*/
function get_file_type_from_mimetype($mimetype) function get_file_type_from_mimetype($mimetype)
{ {
return explode('/', $mimetype)[1]; return explode('/', $mimetype)[1];

View File

@@ -102,7 +102,7 @@ class Demo
$filename = Str::random() . '-' . str_replace(' ', '', $file->getClientOriginalName()); $filename = Str::random() . '-' . str_replace(' ', '', $file->getClientOriginalName());
$thumbnail = null; $thumbnail = null;
$filesize = $file->getSize(); $filesize = $file->getSize();
$filetype = get_file_type($file); $filetype = get_file_type($file->getMimeType());
return [ return [
'id' => random_int(1000, 9999), 'id' => random_int(1000, 9999),

View File

@@ -239,68 +239,58 @@ class Editor
public static function upload($request, $shared = null) public static function upload($request, $shared = null)
{ {
// Get parent_id from request // Get parent_id from request
$folder_id = $request->parent_id === 0 ? 0 : $request->parent_id;
$file = $request->file('file'); $file = $request->file('file');
// Check or create directories // Check or create directories
self::check_directories(['chunks', 'file-manager']); self::check_directories(['chunks', 'file-manager']);
// File name
$user_file_name = basename('chunks/' . substr($file->getClientOriginalName(),17), '.part');
$disk_file_name = basename('chunks/' . $file->getClientOriginalName(), '.part');
$temp_filename = $file->getClientOriginalName();
// Generate file // Generate file
File::append(storage_path() . '/app/chunks/' . $file->getClientOriginalName(), $file->get()); File::append(storage_path() . '/app/chunks/' . $temp_filename, $file->get());
// If last then process file // If last then process file
if ($request->has('is_last') && $request->boolean('is_last')) { if ($request->boolean('is_last')) {
// Get original file name $disk_local = Storage::disk('local');
$original_file_name = basename('chunks/' . $file->getClientOriginalName(), '.part');
// Rename chunk part to original file name in chunk directory
Storage::disk('local')->move('chunks/' . $file->getClientOriginalName(), 'chunks/' . $original_file_name);
// Get user data // Get user data
$user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor'; $user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor';
$user_id = is_null($shared) ? Auth::id() : $shared->user_id; $user_id = is_null($shared) ? Auth::id() : $shared->user_id;
$user_storage_used = user_storage_percentage($user_id, Storage::disk('local')->size('chunks/' . $original_file_name));
// Get storage limitation setup // File Info
$storage_limitation = get_setting('storage_limitation'); $file_size = $disk_local->size('chunks/' . $temp_filename);
$file_mimetype = $disk_local->mimeType('chunks/' . $temp_filename);
// Check if user can upload // Check if user has enough space to upload file
if ($storage_limitation && $user_storage_used >= 100) { self::check_user_storage_capacity($user_id, $file_size, $temp_filename);
// Delete file
Storage::disk('local')->delete('chunks/' . $original_file_name);
// Abort uploading
abort(423, 'You exceed your storage limit!');
}
// File name
$prefixed_file_name = Str::random() . '-' . str_replace(' ', '', $original_file_name);
// Create thumbnail // Create thumbnail
$thumbnail = self::get_image_thumbnail('chunks/' . $original_file_name, $prefixed_file_name, $file); $thumbnail = self::get_image_thumbnail('chunks/' . $temp_filename, $user_file_name);
// Store to disk // Move finished file from chunk to file-manager directory
Storage::disk('local')->move('chunks/' . $original_file_name, 'file-manager/' . $prefixed_file_name); $disk_local->move('chunks/' . $temp_filename, 'file-manager/' . $disk_file_name);
// Store file // Store file
$options = [ $options = [
'name' => pathinfo($file->getClientOriginalName())['filename'], 'mimetype' => get_file_type_from_mimetype($file_mimetype),
'mimetype' => get_file_type_from_mimetype(Storage::disk('local')->mimeType('file-manager/' . $prefixed_file_name)), 'type' => get_file_type($file_mimetype),
'folder_id' => $request->parent_id,
'name' => $user_file_name,
'unique_id' => get_unique_id(), 'unique_id' => get_unique_id(),
'basename' => $disk_file_name,
'user_scope' => $user_scope, 'user_scope' => $user_scope,
'folder_id' => $folder_id,
'thumbnail' => $thumbnail, 'thumbnail' => $thumbnail,
'basename' => $prefixed_file_name, 'filesize' => $file_size,
'filesize' => Storage::disk('local')->size('file-manager/' . $prefixed_file_name),
'type' => get_file_type('file-manager/' . $prefixed_file_name),
'user_id' => $user_id, 'user_id' => $user_id,
]; ];
// Move files to external storage // Move files to external storage
if (! is_storage_driver(['local'])) { if (! is_storage_driver(['local'])) {
self::move_to_external_storage($prefixed_file_name, $thumbnail); self::move_to_external_storage($disk_file_name, $thumbnail);
} }
// Return new file // Return new file
@@ -316,13 +306,15 @@ class Editor
*/ */
private static function move_to_external_storage(string $filename, ?string $thumbnail): void private static function move_to_external_storage(string $filename, ?string $thumbnail): void
{ {
$disk_local = Storage::disk('local');
foreach ([$filename, $thumbnail] as $file) { foreach ([$filename, $thumbnail] as $file) {
// Check if file exist // Check if file exist
if (!$file) continue; if (!$file) continue;
// Get file size // Get file size
$filesize = Storage::disk('local')->size('file-manager/' . $filename); $filesize = $disk_local->size('file-manager/' . $filename);
// If file is bigger than 5.2MB then run multipart upload // If file is bigger than 5.2MB then run multipart upload
if ($filesize > 5242880) { if ($filesize > 5242880) {
@@ -342,16 +334,18 @@ class Editor
'key' => 'file-manager/' . $file 'key' => 'file-manager/' . $file
]); ]);
// Upload content
try { try {
// Upload content
$uploader->upload(); $uploader->upload();
} catch (MultipartUploadException $e) { } catch (MultipartUploadException $e) {
// Write error log
Log::error($e->getMessage()); Log::error($e->getMessage());
// Delete file after error // Delete file after error
Storage::disk('local')->delete('file-manager/' . $file); $disk_local->delete('file-manager/' . $file);
throw new HttpException(409, $e->getMessage()); throw new HttpException(409, $e->getMessage());
} }
@@ -363,7 +357,7 @@ class Editor
} }
// Delete file after upload // Delete file after upload
Storage::disk('local')->delete('file-manager/' . $file); $disk_local->delete('file-manager/' . $file);
} }
} }
@@ -391,34 +385,63 @@ class Editor
/** /**
* Create thumbnail for images * Create thumbnail for images
* *
* @param string $chunk_file_path * @param string $file_path
* @param string $filename * @param string $filename
* @param $file * @param $file
* @return string|null * @return string|null
*/ */
private static function get_image_thumbnail(string $chunk_file_path, string $filename, $file) private static function get_image_thumbnail(string $file_path, string $filename)
{ {
if (in_array(Storage::disk('local')->mimeType($chunk_file_path), ['image/gif', 'image/jpeg', 'image/jpg', 'image/png', 'image/webp'])) { $local_disk = Storage::disk('local');
// Create thumbnail from image
if (in_array($local_disk->mimeType($file_path), ['image/gif', 'image/jpeg', 'image/jpg', 'image/png', 'image/webp'])) {
// Get thumbnail name // Get thumbnail name
$thumbnail = 'thumbnail-' . $filename; $thumbnail = 'thumbnail-' . $filename;
// Create intervention image // Create intervention image
$image = Image::make(storage_path() . '/app/' . $chunk_file_path)->orientate(); $image = Image::make(storage_path() . '/app/' . $file_path)->orientate();
// Resize image // Resize image
$image->resize(564, null, function ($constraint) { $image->resize(512, null, function ($constraint) {
$constraint->aspectRatio(); $constraint->aspectRatio();
})->stream(); })->stream();
// Store thumbnail to disk // Store thumbnail to disk
Storage::disk('local')->put('file-manager/' . $thumbnail, $image); $local_disk->put('file-manager/' . $thumbnail, $image);
}
} elseif (Storage::disk('local')->mimeType($chunk_file_path) === 'image/svg+xml') { // Return thumbnail as svg file
if ($local_disk->mimeType($file_path) === 'image/svg+xml') {
$thumbnail = $filename; $thumbnail = $filename;
} }
return $thumbnail ?? null; return $thumbnail ?? null;
} }
/**
* Check if user has enough space to upload file
*
* @param $user_id
* @param int $file_size
* @param $temp_filename
*/
private static function check_user_storage_capacity($user_id, int $file_size, $temp_filename): void
{
// Get user storage percentage and get storage_limitation setting
$user_storage_used = user_storage_percentage($user_id, $file_size);
$storage_limitation = get_setting('storage_limitation');
// Check if user can upload
if ($storage_limitation && $user_storage_used >= 100) {
// Delete file
Storage::disk('local')->delete('chunks/' . $temp_filename);
// Abort uploading
abort(423, 'You exceed your storage limit!');
}
}
} }

102
config/content.php Normal file
View File

@@ -0,0 +1,102 @@
<?php
return [
'pages' => [
[
'visibility' => 1,
'title' => 'Terms of Service',
'slug' => 'terms-of-service',
'content' => 'Laoreet cum hendrerit iaculis arcu phasellus congue et elementum, pharetra risus imperdiet aptent posuere rutrum parturient blandit, dapibus tellus ridiculus potenti aliquam sociis turpis. Nullam commodo eget laoreet risus cursus vel placerat, in dapibus sociis gravida faucibus sodales, fringilla potenti elit semper iaculis ullamcorper. Dignissim vulputate pretium montes pellentesque mollis, consectetur adipiscing curabitur semper sem rhoncus, litora viverra curae proin.',
],
[
'visibility' => 1,
'title' => 'Privacy Policy',
'slug' => 'privacy-policy',
'content' => 'Sit orci justo augue maecenas laoreet consectetur natoque magnis in viverra sagittis, himenaeos urna facilisis mus proin primis diam accumsan tristique inceptos. Primis quisque posuere sit praesent lobortis feugiat semper convallis facilisis, vivamus gravida ligula nostra curae eu donec duis parturient senectus, arcu dolor viverra penatibus natoque cum nisi commodo. Litora sociis mauris justo nullam suspendisse mattis maecenas nascetur congue phasellus cras ultricies posuere donec, dapibus egestas diam lacus ornare montes senectus tincidunt eu taciti sed consequat.',
],
[
'visibility' => 1,
'title' => 'Cookie Policy',
'slug' => 'cookie-policy',
'content' => 'Metus penatibus ligula dolor natoque non habitasse laoreet facilisis, libero vivamus eget semper vulputate interdum integer, phasellus lorem enim blandit consectetur nullam sollicitudin. Hendrerit interdum luctus ut in molestie himenaeos eros cum laoreet parturient est, eu lectus hac et netus viverra dictumst congue elit sem senectus litora, fames scelerisque adipiscing inceptos fringilla montes sociosqu suscipit auctor potenti. Elementum lacus vulputate viverra ac morbi ligula ipsum facilisi, sit eu imperdiet lacinia congue dis vitae.',
],
],
'content' => [
[
'name' => 'section_features',
'value' => '1',
],
[
'name' => 'section_feature_boxes',
'value' => '1',
],
[
'name' => 'section_pricing_content',
'value' => '1',
],
[
'name' => 'section_get_started',
'value' => '1',
],
[
'name' => 'header_title',
'value' => 'Simple <span style="color: #41B883">&</span> Powerful Personal Cloud Storage',
],
[
'name' => 'header_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
],
[
'name' => 'features_title',
'value' => 'The Fastest Growing <span style="color: #41B883">File Manager</span> on the CodeCanyon Market',
],
[
'name' => 'features_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
],
[
'name' => 'feature_title_1',
'value' => 'Truly Freedom',
],
[
'name' => 'feature_description_1',
'value' => 'You have full control over VueFileManager, no third authorities will control your service or usage, only you.',
],
[
'name' => 'feature_title_2',
'value' => 'The Sky is the Limit',
],
[
'name' => 'feature_description_2',
'value' => 'VueFileManager is cloud storage software. You have to install and running application on your own server hosting.',
],
[
'name' => 'feature_title_3',
'value' => 'No Monthly Fees',
],
[
'name' => 'feature_description_3',
'value' => 'When you running VueFileManager on your own server hosting, anybody can\'t control your content or resell your user data. Your data is safe.',
],
[
'name' => 'pricing_title',
'value' => 'Pick the <span style="color: #41B883;">Best Plan</span> For Your Needs',
],
[
'name' => 'pricing_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
],
[
'name' => 'get_started_title',
'value' => 'Ready to Get <span style="color: #41B883">Started</span><br> With Us?',
],
[
'name' => 'get_started_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
],
[
'name' => 'footer_content',
'value' => '© 2020 Simple & Powerfull Personal Cloud Storage. Developed by <a href="https://hi5ve.digital" target="_blank">Hi5Ve.Digital</a>',
],
],
];

View File

@@ -6,104 +6,4 @@ return [
// Define size of chunk uploaded by MB. E.g. integer 128 means chunk size will be 128MB. // Define size of chunk uploaded by MB. E.g. integer 128 means chunk size will be 128MB.
'chunk_size' => '128', 'chunk_size' => '128',
'pages' => [
[
'visibility' => 1,
'title' => 'Terms of Service',
'slug' => 'terms-of-service',
'content' => 'Laoreet cum hendrerit iaculis arcu phasellus congue et elementum, pharetra risus imperdiet aptent posuere rutrum parturient blandit, dapibus tellus ridiculus potenti aliquam sociis turpis. Nullam commodo eget laoreet risus cursus vel placerat, in dapibus sociis gravida faucibus sodales, fringilla potenti elit semper iaculis ullamcorper. Dignissim vulputate pretium montes pellentesque mollis, consectetur adipiscing curabitur semper sem rhoncus, litora viverra curae proin.',
],
[
'visibility' => 1,
'title' => 'Privacy Policy',
'slug' => 'privacy-policy',
'content' => 'Sit orci justo augue maecenas laoreet consectetur natoque magnis in viverra sagittis, himenaeos urna facilisis mus proin primis diam accumsan tristique inceptos. Primis quisque posuere sit praesent lobortis feugiat semper convallis facilisis, vivamus gravida ligula nostra curae eu donec duis parturient senectus, arcu dolor viverra penatibus natoque cum nisi commodo. Litora sociis mauris justo nullam suspendisse mattis maecenas nascetur congue phasellus cras ultricies posuere donec, dapibus egestas diam lacus ornare montes senectus tincidunt eu taciti sed consequat.',
],
[
'visibility' => 1,
'title' => 'Cookie Policy',
'slug' => 'cookie-policy',
'content' => 'Metus penatibus ligula dolor natoque non habitasse laoreet facilisis, libero vivamus eget semper vulputate interdum integer, phasellus lorem enim blandit consectetur nullam sollicitudin. Hendrerit interdum luctus ut in molestie himenaeos eros cum laoreet parturient est, eu lectus hac et netus viverra dictumst congue elit sem senectus litora, fames scelerisque adipiscing inceptos fringilla montes sociosqu suscipit auctor potenti. Elementum lacus vulputate viverra ac morbi ligula ipsum facilisi, sit eu imperdiet lacinia congue dis vitae.',
],
],
'content' => [
[
'name' => 'section_features',
'value' => '1',
],
[
'name' => 'section_feature_boxes',
'value' => '1',
],
[
'name' => 'section_pricing_content',
'value' => '1',
],
[
'name' => 'section_get_started',
'value' => '1',
],
[
'name' => 'header_title',
'value' => 'Simple <span style="color: #41B883">&</span> Powerfull Personal Cloud Storage',
],
[
'name' => 'header_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
],
[
'name' => 'features_title',
'value' => 'The Fastest Growing <span style="color: #41B883">File Manager</span> on the CodeCanyon Market',
],
[
'name' => 'features_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
],
[
'name' => 'feature_title_1',
'value' => 'Truly Freedom',
],
[
'name' => 'feature_description_1',
'value' => 'You have full control over VueFileManager, no third authorities will control your service or usage, only you.',
],
[
'name' => 'feature_title_2',
'value' => 'The Sky is the Limit',
],
[
'name' => 'feature_description_2',
'value' => 'VueFileManager is cloud storage software. You have to install and running application on your own server hosting.',
],
[
'name' => 'feature_title_3',
'value' => 'No Monthly Fees',
],
[
'name' => 'feature_description_3',
'value' => 'When you running VueFileManager on your own server hosting, anybody can\'t control your content or resell your user data. Your data is safe.',
],
[
'name' => 'pricing_title',
'value' => 'Pick the <span style="color: #41B883;">Best Plan</span> For Your Needs',
],
[
'name' => 'pricing_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
],
[
'name' => 'get_started_title',
'value' => 'Ready to Get <span style="color: #41B883">Started</span><br> With Us?',
],
[
'name' => 'get_started_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
],
[
'name' => 'footer_content',
'value' => '© 2020 Simple & Powerfull Personal Cloud Storage. Developed by <a href="https://hi5ve.digital" target="_blank">Hi5Ve.Digital</a>',
],
],
]; ];

2
public/js/main.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -238,6 +238,7 @@
flex: 0 0 100%; flex: 0 0 100%;
@include transition(150ms); @include transition(150ms);
position: relative; position: relative;
scroll-behavior: smooth;
&.is-fileinfo-visible { &.is-fileinfo-visible {
flex: 0 1 100%; flex: 0 1 100%;

View File

@@ -2,24 +2,39 @@
<transition name="info-panel"> <transition name="info-panel">
<div v-if="uploadingFilesCount" class="upload-progress"> <div v-if="uploadingFilesCount" class="upload-progress">
<div class="progress-title"> <div class="progress-title">
<span>{{ $t('uploading.progress', {current:uploadingFilesCount.current, total: uploadingFilesCount.total, progress: uploadingFileProgress}) }}</span> <span v-if="isProcessingFile">
<refresh-cw-icon size="12" class="sync-alt"></refresh-cw-icon>
{{ $t('uploading.processing_file') }}
</span>
<span v-if="!isProcessingFile && uploadingFilesCount.total === 1">
{{ $t('uploading.progress_single_upload', {progress: uploadingFileProgress}) }}
</span>
<span v-if="!isProcessingFile && uploadingFilesCount.total > 1">
{{ $t('uploading.progress', {current:uploadingFilesCount.current, total: uploadingFilesCount.total, progress: uploadingFileProgress}) }}
</span>
</div> </div>
<ProgressBar :progress="uploadingFileProgress"/> <ProgressBar :progress="uploadingFileProgress" />
</div> </div>
</transition> </transition>
</template> </template>
<script> <script>
import ProgressBar from '@/components/FilesView/ProgressBar' import ProgressBar from '@/components/FilesView/ProgressBar'
import { RefreshCwIcon } from 'vue-feather-icons'
import {mapGetters} from 'vuex' import {mapGetters} from 'vuex'
export default { export default {
name: 'UploadProgress', name: 'UploadProgress',
components: { components: {
RefreshCwIcon,
ProgressBar, ProgressBar,
}, },
computed: { computed: {
...mapGetters(['uploadingFileProgress', 'uploadingFilesCount']) ...mapGetters([
'uploadingFileProgress',
'uploadingFilesCount',
'isProcessingFile',
])
} }
} }
</script> </script>
@@ -28,6 +43,24 @@
@import '@assets/vue-file-manager/_variables'; @import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins'; @import '@assets/vue-file-manager/_mixins';
.sync-alt {
animation: spin 1s linear infinite;
margin-right: 5px;
polyline, path {
stroke: $theme;
}
}
@keyframes spin {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
.info-panel-enter-active, .info-panel-enter-active,
.info-panel-leave-active { .info-panel-leave-active {
transition: all 0.3s ease; transition: all 0.3s ease;

View File

@@ -73,28 +73,31 @@ const Helpers = {
this.$store.dispatch('createFolder', folderName) this.$store.dispatch('createFolder', folderName)
} }
Vue.prototype.$uploadFiles = async function (files) { Vue.prototype.$handleUploading = async function (files, parent_id) {
// Prevent submit empty files
if (files && files.length == 0) return
let fileCount = files ? files.length : 0 let fileBuffer = []
let fileCountSucceed = 1
// Append the file list to fileBuffer array
Array.prototype.push.apply(fileBuffer, files);
let fileSucceed = 0
// Update files count in progressbar
store.commit('UPDATE_FILE_COUNT_PROGRESS', { store.commit('UPDATE_FILE_COUNT_PROGRESS', {
current: fileCountSucceed, current: fileSucceed,
total: fileCount total: files.length
}) })
// Reset upload progress to 0
store.commit('UPLOADING_FILE_PROGRESS', 0) store.commit('UPLOADING_FILE_PROGRESS', 0)
// Get parent id // Get parent id
const rootFolder = this.$store.getters.currentFolder let parentFolder = this.$store.getters.currentFolder ? this.$store.getters.currentFolder.unique_id : 0
? this.$store.getters.currentFolder.unique_id let rootFolder = parent_id ? parent_id : parentFolder
: 0
for (var i = files.length - 1; i >= 0; i--) { // Upload files
do {
let file = files[i], let file = fileBuffer.shift(),
chunks = [] chunks = []
// Calculate ceils // Calculate ceils
@@ -108,21 +111,23 @@ const Helpers = {
)); ));
} }
// Set form Data // Set Data
let formData = new FormData() let formData = new FormData(),
let uploadedSize = 0 uploadedSize = 0,
isNotGeneralError = true
do { do {
let isLast = chunks.length === 1 let isLast = chunks.length === 1,
let chunk = chunks.shift() chunk = chunks.shift(),
let attempts = 0 attempts = 0,
filename = Array(16).fill(0).map(x => Math.random().toString(36).charAt(2)).join('') + '-' + file.name + '.part'
// Set form data // Set form data
formData.set('is_last', isLast); formData.set('file', chunk, filename);
formData.set('file', chunk, `${file.name}.part`);
formData.set('parent_id', rootFolder) formData.set('parent_id', rootFolder)
formData.set('is_last', isLast);
// Upload data // Upload chunks
do { do {
await store.dispatch('uploadFiles', { await store.dispatch('uploadFiles', {
form: formData, form: formData,
@@ -130,35 +135,39 @@ const Helpers = {
totalUploadedSize: uploadedSize totalUploadedSize: uploadedSize
}).then(() => { }).then(() => {
uploadedSize = uploadedSize + chunk.size uploadedSize = uploadedSize + chunk.size
}).catch(() => { }).catch((error) => {
// Count attempts
attempts++ attempts++
if (attempts === 3) { // Break uploading proccess
events.$emit('alert:open', { if (error.response.status === 500)
title: this.$t('popup_error.title'), isNotGeneralError = false
message: this.$t('popup_error.message'),
})
}
})
} while (attempts !== 0 && attempts !== 3)
} while (chunks.length !== 0) // Show Error
if (attempts === 3)
this.$isSomethingWrong()
})
} while (isNotGeneralError && attempts !== 0 && attempts !== 3)
} while (isNotGeneralError && chunks.length !== 0)
fileSucceed++
// Progress file log // Progress file log
store.commit('UPDATE_FILE_COUNT_PROGRESS', { store.commit('UPDATE_FILE_COUNT_PROGRESS', {
current: fileCountSucceed, current: fileSucceed,
total: fileCount total: files.length
}) })
// Uploading finished } while (fileBuffer.length !== 0)
if (fileCount === fileCountSucceed) {
store.commit('UPDATE_FILE_COUNT_PROGRESS', undefined) store.commit('UPDATE_FILE_COUNT_PROGRESS', undefined)
} else { }
// Add uploaded file
fileCountSucceed++ Vue.prototype.$uploadFiles = async function (files) {
}
} this.$handleUploading(files, undefined)
} }
Vue.prototype.$uploadExternalFiles = async function (event, parent_id) { Vue.prototype.$uploadExternalFiles = async function (event, parent_id) {
@@ -167,44 +176,9 @@ const Helpers = {
if (event.dataTransfer.items.length == 0) return if (event.dataTransfer.items.length == 0) return
// Get files // Get files
const files = [...event.dataTransfer.items].map(item => item.getAsFile()); let files = [...event.dataTransfer.items].map(item => item.getAsFile());
let fileCountSucceed = 1 this.$handleUploading(files, parent_id)
store.commit('UPDATE_FILE_COUNT_PROGRESS', {
current: fileCountSucceed,
total: files.length
})
for (var i = files.length - 1; i >= 0; i--) {
let formData = new FormData()
// Append data
formData.append('file', files[i])
// Append form data
formData.append('parent_id', parent_id)
// Upload data
await store.dispatch('uploadFiles', formData).then(() => {
// Progress file log
store.commit('UPDATE_FILE_COUNT_PROGRESS', {
current: fileCountSucceed,
total: files.length
})
// Progress file log
store.commit('INCREASE_FOLDER_ITEM', parent_id)
// Uploading finished
if (files.length === fileCountSucceed) {
store.commit('UPDATE_FILE_COUNT_PROGRESS', undefined)
} else {
// Add uploaded file
fileCountSucceed++
}
})
}
} }
Vue.prototype.$downloadFile = function (url, filename) { Vue.prototype.$downloadFile = function (url, filename) {

View File

@@ -680,7 +680,9 @@
"href": "Please confirm your payment." "href": "Please confirm your payment."
}, },
"uploading": { "uploading": {
"progress": "上传文件 {current}/{total}" "processing_file": "Processing File...",
"progress_single_upload": "上传文件 {progress}%",
"progress": "上传文件 {progress}% - {current}/{total}"
}, },
"user_add_card": { "user_add_card": {
"default_description": "", "default_description": "",

View File

@@ -680,6 +680,8 @@
"href": "Please confirm your payment." "href": "Please confirm your payment."
}, },
"uploading": { "uploading": {
"processing_file": "Processing File...",
"progress_single_upload": "Uploading File {progress}%",
"progress": "Uploading File {progress}% - {current}/{total}" "progress": "Uploading File {progress}% - {current}/{total}"
}, },
"user_add_card": { "user_add_card": {

View File

@@ -680,7 +680,9 @@
"href": "Prosím potvrďte Vašu platbu." "href": "Prosím potvrďte Vašu platbu."
}, },
"uploading": { "uploading": {
"progress": "Nahrávam súbory {current}/{total}" "processing_file": "Spracuvávam súbor...",
"progress_single_upload": "Nahrávam súbor {progress}%",
"progress": "Nahrávam súbory {progress}% - {current}/{total}"
}, },
"user_add_card": { "user_add_card": {
"default_description": "Vaša karta bude uložená a použitá pre platbu ako prvá.", "default_description": "Vaša karta bude uložená a použitá pre platbu ako prvá.",

View File

@@ -8,6 +8,7 @@ const defaultState = {
fileInfoDetail: undefined, fileInfoDetail: undefined,
currentFolder: undefined, currentFolder: undefined,
uploadingFileProgress: 0, uploadingFileProgress: 0,
isProcessingFile: false,
navigation: undefined, navigation: undefined,
isSearching: false, isSearching: false,
browseHistory: [], browseHistory: [],
@@ -285,11 +286,15 @@ const mutations = {
STORE_CURRENT_FOLDER(state, folder) { STORE_CURRENT_FOLDER(state, folder) {
state.currentFolder = folder state.currentFolder = folder
}, },
PROCESSING_FILE(state, status) {
state.isProcessingFile = status
}
} }
const getters = { const getters = {
uploadingFileProgress: state => state.uploadingFileProgress, uploadingFileProgress: state => state.uploadingFileProgress,
uploadingFilesCount: state => state.uploadingFilesCount, uploadingFilesCount: state => state.uploadingFilesCount,
isProcessingFile: state => state.isProcessingFile,
fileInfoDetail: state => state.fileInfoDetail, fileInfoDetail: state => state.fileInfoDetail,
currentFolder: state => state.currentFolder, currentFolder: state => state.currentFolder,
browseHistory: state => state.browseHistory, browseHistory: state => state.browseHistory,

View File

@@ -42,6 +42,8 @@ const actions = {
.then(response => { .then(response => {
commit('ADD_NEW_FOLDER', response.data) commit('ADD_NEW_FOLDER', response.data)
events.$emit('scrollTop')
if ( getters.currentFolder.location !== 'public' ) { if ( getters.currentFolder.location !== 'public' ) {
dispatch('getAppData') dispatch('getAppData')
} }
@@ -87,14 +89,17 @@ const actions = {
}, },
onUploadProgress: event => { onUploadProgress: event => {
let loaded = totalUploadedSize + event.loaded var percentCompleted = Math.floor(((totalUploadedSize + event.loaded) / fileSize) * 100)
var percentCompleted = Math.floor((loaded * 100) / fileSize) commit('UPLOADING_FILE_PROGRESS', percentCompleted >= 100 ? 100 : percentCompleted)
commit('UPLOADING_FILE_PROGRESS', percentCompleted) if (percentCompleted >= 100) {
commit('PROCESSING_FILE', true)
}
} }
}) })
.then(response => { .then(response => {
commit('PROCESSING_FILE', false)
// Check if user is in uploading folder, if yes, than show new file // Check if user is in uploading folder, if yes, than show new file
if (response.data.folder_id == getters.currentFolder.unique_id) if (response.data.folder_id == getters.currentFolder.unique_id)
@@ -103,6 +108,8 @@ const actions = {
resolve(response) resolve(response)
}) })
.catch(error => { .catch(error => {
commit('PROCESSING_FILE', false)
reject(error) reject(error)
switch (error.response.status) { switch (error.response.status) {