I have a requirement to upload a document to server, since it is a personal one, they want it to be uploaded outside public folder. I know how to upload a file:
if ($model->load(Yii::$app->request->post())) {
$model->document = UploadedFile::getInstance($model, 'document');
if ($model->upload() !== false) {
$model->save();
}
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
But how do I read it since a Url will not be able to access it? I was planning to create an action to get the file but not sure if Yii has something ready for this case?
Yes Yii can send file output, but you still have to create your own action.
Lets assume following code in siteController Specific to image output, You can use same way to output other files.
public function actionImage($image_path) {
Yii::$app->getResponse()->sendFile(Yii::getAlias('#image_uploads') . $image_path);
}
now image src will be something like
<img src="/site/image?image_path=/posts/1.png" /> or equivalent to the real application url routes
So basic function to send file output by Yii2 is
Yii::$app->getResponse()->sendFile();
Located Under
\yii\web\Response.php
Related
I have build a laravel application where I have some files on public/files directory. if I give this link to others such as download Link, they have chance to know my directory ..
Suppose the link i have to give download link as
www.abc.com/files/45454553535.zip
But i don't want to let Users know that it's there in files directory. So How Do i hide the directory?
Keep your files in the storage directory. That way you can serve the file to the users through code.
Try to follow the documentation: https://laravel.com/docs/5.4/filesystem
I don't know whether this would work or not but giving you an idea. Create a php file use like this:
header('Content-Type: application/zip');
$a=file_get_contents(file.zip)
echo $a;
From this user will not know from where the contents are fetched.
Try this.
public function getDownload()
{
$filename='45454553535.zip'
$file= public_path(). "/files/".$filename;
$headers = array(
'Content-Type: application/zip',
);
return Response::download($file, $filename, $headers);
}
".files/45454553535.zip"will not work as you have to give full physical path.
Update 20/05/2016
Laravel 5, 5.1, 5.2 or 5.* users can use the following method instead of Response facade. However, my previous answer will work for both Laravel 4 or 5.
return response()->download($file, $filename, $headers);
You can just create a your controller and route.
Route::get('files/{filename}', [
'as' => 'file.get',
'uses' => 'FileController#get',
]);
Controller should check your proper directory. Try to keep your files in storage path, not public.
class FileController extends Controller
{
private $path;
public function __construct()
{
$path = storage_path()
. '/your-valid-directory/';
}
public function get($filename)
{
$file_path = $this->path
. filter_var($filename, FILTER_SANITIZE_STRING);
if (file_exists($file_path) && is_readable($file_path)) {
return response(file_get_contents($file_path), 200, [
'Content-Type: application/zip',
]);
} else {
abort(404);
}
}
}
Now you can get access to specific file by:
{{ route('file.get', ['filename' => '45454553535.zip') }}
This action generate link looks like: your-domain.com/files/45454553535.zip. :)
Anyway in my opinion - in the future just make file factory with specific headers, directories.
Good luck!
I'm writing an app that lets the user upload an Excel file. It checks the file for errors, then, if no errors are found, it uploads the contents to a database. If it does find errors, the cells containing errors are colored red, then the file is saved. I then want to create a download link to this file so the user can check where they made mistakes.
The problem is that I am not sure how to create this link and where to store the file. I modify the file like this:
foreach ($badCells as $bcell) {
$sheet->getStyle($bcell)->applyFromArray(array(
'fill' => array(
'type' => \PHPExcel_Style_Fill::FILL_SOLID,
'color' => array('rgb' => 'FF4444')
)
));
}
And then save it with
$objWriter->save($dldir . $formData['upload']['name']);
$dldir is created with
$dldir = "/download/";
if (file_exists($dldir)) {
if (!is_dir($dldir)) {
unlink($dldir);
mkdir($dldir);
}
} else {
if(!is_dir($dldir)) {
mkdir($dldir);
}
}
Is this even the right way to do this? Can I store the files in any old folder or do they have go somewhere specific? How do I create the link to the specific file in the view for the user and make it accessible so they can download it?
I may be wrong .
For file upload i use this library Gargron/fileupload
The function bellow help to upload file to a specific folder and return full link of the file. You can save the link in DB
function uploadFile ($file){
$validator = new \FileUpload\Validator\Simple('5M');
$pathresolver = new \FileUpload\PathResolver\Simple($_SERVER['DOCUMENT_ROOT'].'/upload_folder');
$filesystem = new \FileUpload\FileSystem\Simple();
$fileupload = new \FileUpload\FileUpload(file, $_SERVER);
$fileupload->setPathResolver($pathresolver);
$fileupload->setFileSystem($filesystem);
$fileupload->addValidator($validator);
$md5Generator = new \FileUpload\FileNameGenerator\MD5 (true);
$fileupload->setFileNameGenerator($md5Generator);
list($files) = $fileupload->processAll();
foreach($files as $file){
if ($file->completed) {
return $_SERVER['DOCUMENT_ROOT'].'/upload_folder/'.$file->getFileName()) ;
}
}
}
Thanks for the help, I managed to figure it out over the weekend. Here's how I did
it:
$filename = join(DIRECTORY_SEPARATOR, array($dldir, $formData['upload']['name']));
$objWriter->save(str_replace(__FILE__,$filename,__FILE__));
This is how I save the file, using DIRECTORY_SEPARATOR so it will work correctly on both Windows and Linux.
return $this->redirect()->toRoute('import', ['action' => 'reject'],['query' => ['q' => 'file', 'name' => $filename]]);
In the controller, I redirect the route to the correct action and pass the file name on to it via a query URL.
public function rejectAction()
{
$filename = $this->getRequest()->getQuery('name', null);
return new ViewModel(array('filename' => $filename));
}
There, I obtain said file name through getRequest()->getQuery() and pass it on to the viewmodel.
Right-click here and choose 'Save As' to download your file.
And finally, this is how it shows the link in reject.phtml.
Downloading only works with right click and save as, I suspect I will have to write some sort of file handler to make ZF produce the correct headers for a normal left click download.
I've got an idea for a project and wondering if there's an already existing module that can achieve something similar. Basically I want to allow the users to upload/download files from their member profile. Users will only be able to view/download their own files. I have member profiles set up already, it's the file handling that I need to get working now.
I imagine it will be something like a gridfield displayed on the users profile. When they use an upload field it will save files in to assets/ folder. Which can only be viewed by that member (admins can view all in the /admin section of the CMS.
Are there any current modules that support something like this?
If I create an extension of the Member profile that allows uploading files, how could I implement the security for only users with the currentID to view files?
EDIT: OK So I've made some progress with this. The upload file function works. It saves it in to a folder which is named after the user. However, it saves the uploaded file in that directory, but also a blank file under the "Files" directory. Here is my code...
//User file upload function
public function UploadUserFile() {
$fields = new FieldList($field = new UploadField('UserFiles', 'Upload files'));
$field->setCanAttachExisting(false); // Block access to SilverStripe assets library
$field->setCanPreviewFolder(false); // Don't show target filesystem folder on upload field
$field->setFolderName('user-files/user-'.Member::currentUserID()); //Upload to a user specific folder
$actions = new FieldList(new FormAction('submit', 'Save Images'));
$form = new Form($this, 'UploadUserFile', $fields, $actions, null);
return $form;
}
public function submit($data, $form) {
$file = new File();
$form->saveInto($file);
$file->write();
return $this;
}
EDIT 2:
Turns out that the submit function was causing the extra file. Disregard. Still experiencing other problems now but will see if I can fix it first.
EDIT 3:
OK, I've had a go and I am able to display files on the front end. Here is my code...
//User file upload function
public function UploadUserFile() {
$fields = new FieldList($field = new UploadField('UserFiles', 'Upload files'));
$field->setCanAttachExisting(false); // Block access to SilverStripe assets library
$field->setCanPreviewFolder(false); // Don't show target filesystem folder on upload field
$field->setFolderName('User-Files/User-'.Member::currentUserID().'-'.Member::currentUser()->Surname.Member::currentUser()->FirstName); //Upload to a user specific folder
$field->setAutoUpload(false);
$actions = new FieldList(new FormAction('submit', 'Save Images'));
$form = new Form($this, 'UploadUserFile', $fields, $actions, null);
return $form;
}
//Refresh files function
public function submit($data, $form) {
return $this->redirectBack();
}
//Display users files
public function DisplayFiles() {
$arrayList = ArrayList::create();
$files = File::get()->filter(array(
"OwnerID" => Member::currentUserID(),
"ClassName:not" => 'Folder'
));
foreach($files as $file) {
if($file->canView()) {
$arrayList->push($file);
}
}
return $arrayList;
}
I'm trying to allow users to delete their file from the front end. What's the best way to handle this?
Thanks
Yes. Use the secureassets module! https://github.com/silverstripe-labs/silverstripe-secureassets
I need to send an image to server via an ajax request and it gets through just fine
and in my controller I can just use $_FILES["image"] to do stuff to it.
But I need to validate the image before I save it.
And in the Yii this can be achieved by doing something like this
$file = CUploadedFile::getInstance($model,'image');
if($model->validated(array('image'))){
$model->image->saveAs(Yii::getPathOfAlias('webroot') . '/upload/user_thumb/' . $model->username.'.'.$model->photo->extensionName);
}
But the problem is I don't have a $model, all I have is $_FILES["image"], now what should I put instead of the $model???
is there any other way where I can validate and save files without creating a model and just by Using $_FILES["image"]?
thanks for this awesome community... :)
Exists many ways how you can do upload. I want offer to you one of them.
1.You need to create model for your images.
class Image extends CActiveRecord {
//method where need to specify validation rules
public function rules()
{
return [
['filename', 'length', 'max' => 40],
//other rules
];
}
//this function allow to upload file
public function doUpload($insName)
{
$file = CUploadedFile::getInstanceByName($insName);
if ($file) {
$file->saveAs(Yii::getPathOfAlias('webroot').'/upload/user_thumb/'.$this->filename.$file->getExtensionName());
} else {
$this->addError('Please, select at least one file'); // for example
}
}
}
2.Now, need to create controller, where you will do all actions.
class ImageController extends CController {
public function actionUpload()
{
$model = new Image();
if (Yii::app()->request->getPost('upload')) {
$model->filename = 'set filename';
$insName = 'image'; //if you try to upload from $_FILES['image']
if ($model->validate() && $model->doUpload($insName)) {
//upload is successful
} else {
//do something with errors
$errors = $model->getErrors();
}
}
}
}
Creating a model might be overkill in some instances.
The $_FILE supervariable is part of the HTTP mechanism.
You can handle the copy by using the native PHP function move_uploaded_file()
$fileName = "/uploads/".myimage.jpg";
unlink($fileName);
move_uploaded_file($_FILES['Filedata']['tmp_name'], $fileName);
However, you lose the niceties of using a library that provides additional functionality and checks (eg file type and file size limitations).
I have a file upload form in the frontend.
At the moment, when a new record is created, the file is uploaded to
%sf_data_dir%/files/
but due to some business logic I need the file to be uploaded to
%sf_data_dir%/files/%record_id%/
Therefore the uploaded file should be saved AFTER the record is created.
How can I achieve that?
If you use file upload, your form certainly make use of the sfValidatorFile (if not, that's wrong):
$this->validatorSchema['image'] = new sfValidatorFile(array(
'required' => true,
'mime_types' => 'web_images',
));
This validator return a sfValidatedFile instance that can be saved anywhere you want (it's safer than move_uploaded_file, there is checks on the directory, filename...).
In your action (or in the form, as you want/need), you can now do this:
protected function processForm(sfWebRequest $request, sfForm $form)
{
$form->bind(
$request->getParameter($form->getName()),
$request->getFiles($form->getName())
);
if ($form->isValid())
{
$job = $form->save();
// Saving the file to filesystem
$file = $form->getValue('my_upload_field');
$file->save('/path/to/save/'.$job->getId().'/myimage.'.$file->getExtension());
$this->redirect('job_show', $job);
}
}
Don't hesitate to open sfValidatedFile to see how it work.