简体   繁体   中英

Yii2 file upload returns UPLOAD_ERR_PARTIAL

Images fail to upload when I create a new item but on updating the newly created item the file is successfully uploaded. Any reasons?
Trending News Model

class TrendingNews extends \yii\db\ActiveRecord
{
    const EVENT_ADD_TRENDING_NEWS = 'add-trending-news';
      public $featuredFile;

      public function init() {
         $this->on(self::EVENT_ADD_TRENDING_NEWS, [$this, 'saveToLog']);
      }
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'trending_news';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['headline_text','news_info', 'news_url', 'author', 'published', 'date_added', 'date_modified'], 'required'],
            [['headline_text', 'image_url', 'news_info', 'news_url'], 'string'],
            [['date_added', 'date_modified'], 'safe'],
            [['author'], 'string', 'max' => 20],
            [['published'], 'string', 'max' => 2],
            [['featuredFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'headline_text' => 'Headline',
            'image_url' => 'Image Url',
            'news_info' => 'Source',
            'news_url' => 'News Url',
            'author' => 'Author',
            'published' => 'Published',
            'date_added' => 'Date Added',
            'date_modified' => 'Date Modified',
            'featuredFile' => 'Featured Image',
        ];
    }

    public function uploadBanner()
    {
        if ($this->validate()) {
           $ymd = date("Ymd");
           $save_path = \Yii::getAlias('@backend') . '/web/uploads/' . $ymd . '/';
            if (!file_exists($save_path)) {
                mkdir($save_path, 0777, true);
            }
            $fileName = "trending_".Yii::$app->security->generateRandomString(20);
            $this->featuredFile->saveAs($save_path . $fileName .'.' . $this->featuredFile->extension);
            $this->image_url = $ymd . '/'. $fileName . '.' . $this->featuredFile->extension;
            return true;
        } else {
            return false;
        }
    }

    public function upload()
    {
        if ($this->validate()) {
           $ymd = date("Ymd");
           $save_path = \Yii::getAlias('@backend') . '/web/uploads/' . $ymd . '/';
            if (!file_exists($save_path)) {
                mkdir($save_path, 0777, true);
            }

            $fileName = Yii::$app->security->generateRandomString(20);

            $this->featuredFile->saveAs($save_path . $fileName .'.' . $this->featuredFile->extension);
            $this->image_url = $ymd . '/'. $fileName . '.' . $this->featuredFile->extension;
            return true;
        } else {
            return false;
        }
    }

    public function beforeSave($insert)
    {
        if (parent::beforeSave($insert)) {
            if ($this->isNewRecord) {
              $this->date_added = date("YmdHis");
              $this->author =   Yii::$app->user->identity->id;
            } 
            else {
               $this->date_modified = date("YmdHis"); 
            }
            return true;
        }
        else{
            return false;
        }
    }

    public function saveToLog($event)
    {
       //assigning attributes
       // echo 'mail sent to admin using the event';
       $app_log_model = new AppLog();
       $app_log_model->log_time = date("YmdHis");
       $app_log_model->log_activity = 'Added a trending news';
       $app_log_model->user_id = Yii::$app->user->identity->id;
       $app_log_model->device = "1";
       if ($app_log_model->save()) {
           return true;
       } else {
           return $app_log_model->getErrors() ;
       }
    }
}

usage

public function actionCreate()
{
    $model = new TrendingNews();

    if ($model->load(Yii::$app->request->post())) {
        $model->featuredFile = UploadedFile::getInstance($model, 'featuredFile');

        if(isset($model->featuredFile)){
            $model->upload();
            $model->save(false);
        } else {
            // file is uploaded successfully
            $model->save();
            //$model->trigger(BlogArticle::EVENT_EDIT_ARTICLE);
        }
        return $this->redirect(['view', 'id' => $model->id]);
    } else {
        return $this->render('create', [
            'model' => $model,
        ]);
    }
}
public function actionUpdate($id)
{
    $model = $this->findModel($id);

    if ($model->load(Yii::$app->request->post())) {
        $model->featuredFile = UploadedFile::getInstance($model, 'featuredFile');
        if(isset($model->featuredFile)){
            $model->upload();
            $model->save(false);
        } else {
            $model->save();
        }
        return $this->redirect(['view', 'id' => $model->id]);
    } else {
        return $this->render('update', [
            'model' => $model,
        ]);
    }
}

the app log shows this

$_FILES = [
    'TrendingNews' => [
        'name' => [
            'featuredFile' => 'another_evento.jpg'
        ]
        'type' => [
            'featuredFile' => ''
        ]
        'tmp_name' => [
            'featuredFile' => ''
        ]
        'error' => [
            'featuredFile' => 3
        ]
        'size' => [
            'featuredFile' => 0
        ]
    ]
]

featuredFile is not a database field Is there something that is being done wrong? I am using xampp,tried on a live server and the issue is same

You are doing it wrong if you have to require a file input on insert and not on the update you should add a scenario for it so that it asks you to provide file when adding a new record only.

Secondly you are using the same ActiveRecord model and added a custom attribute in it to be used as a file input and in the upload() function you are calling validate() and returning false if not validated but on the same time you are not checking for true or false in you controller action and on the very next line you are calling $model->save(false) with false so technically your script would never intimate the user if there is any validation error.

The file should be uploaded after the record is saved not before saving the record so that there are no extra files uploaded in case of error, although you want the file to be required on insert so technically if file is not uploaded the record should not be saved, and for this reason we have transactions for database insertions you should use a transaction block to save the rcord and the file along with it

You should only call the validate() method for file uploading if you have a separate FormModel for file uploading otherwise you should load the instance from the UploadedFile to the model field and call the $model->save() which will automatically validate the model before saving you should just check for the empty filename before uploading so that when you are updating any record and not submitting a file the previous file should be kept as is.

You need to update your validation rules to the following first

/**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['headline_text','news_info', 'news_url', 'author', 'published', 'date_added', 'date_modified'], 'required'],
            [['headline_text', 'image_url', 'news_info', 'news_url'], 'string'],
            [['date_added', 'date_modified'], 'safe'],
            [['author'], 'string', 'max' => 20],
            [['published'], 'string', 'max' => 2],
             [ [ 'featuredFile' ] , 'required' , 'on' => 'insert' ] ,
            [ [ 'featuredFile' ] , 'file' , 'extensions' => 'png, jpg' , 'maxSize' => 200000 , 'tooBig' => 'Limit is 500KB' ] ,
        ];
    }

Change the upload() function to the following

 public function upload( $ymd , $fileName ) {
        if ( $this->featuredFile !== null && $this->featuredFile->name !== '' ) {
            $save_path = \Yii::getAlias ( '@backend' ) . '/web/uploads/' . $ymd . '/';
            if ( !file_exists ( $save_path ) ) {
                mkdir ( $save_path , 0777 , true );
            }

            if ( !$this->featuredFile->saveAs ( $save_path . $fileName ) ) {
                $this->addError ( 'featuredFile' , 'File could not be uploaded' );
                throw new \Exception ( 'File upload error' );
            }
        }
    }

Then add another method inside your model to remove old file in case of new file uploaded on update

 public function unlinkOldFile( $filename ) {
        if ( $filename !== '' ) {
            $save_path = \Yii::getAlias ( '@backend' ) . '/web/uploads/' . $filename;
            unlink ( $save_path );
        }
    }

after that change your create and update actions to the following so that they use transation blocks for database operations

public function actionCreate() {
    $model = new TrendingNews(['scenario'=>'insert']);

    if ( $model->load ( Yii::$app->request->post () ) ) {

        $model->featuredFile = UploadedFile::getInstance ( $model , 'featuredFile' );

        if ( $model->featuredFile !== null ) {
            $ymd = date ( "Ymd" );
            $fileName = Yii::$app->security->generateRandomString ( 20 ) . '.' . $model->featuredFile->extension;
            $model->image_url = $ymd . '/' . $fileName;
        }


        $transaction = Yii::$app->db->beginTransaction ();

        try {
            if ( !$model->save () ) {
                throw new \Exception ( 'Error Occoured' );
            }

            $model->upload ( $ymd , $fileName );

            $transaction->commit ();

            return $this->redirect ( [ 'view' , 'id' => $model->id ] );
        } catch ( \Exception $ex ) {
            $transaction->rollBack ();
        }
    }
    return $this->render ( 'create' , [
        'model' => $model ,
        ] );
}

public function actionUpdate( $id ) {
    $model = $this->findModel ( $id );

    if ( $model->load ( Yii::$app->request->post () ) ) {
        $model->featuredFile = UploadedFile::getInstance ( $model , 'featuredFile' );

        //$oldFile = '';
        $oldFile = $model->image_url;

        if ( $model->featuredFile !== null ) {

            $ymd = date ( "Ymd" );

            $fileName = Yii::$app->security->generateRandomString ( 20 ) . '.' . $model->featuredFile->extension;

            $model->image_url = $ymd . '/' . $fileName;

        }

        $transaction = Yii::$app->db->beginTransaction ();

        try {
            if ( !$model->save () ) {
                throw new \Exception ( 'Model error' );
            }

            $model->upload ( $ymd , $fileName );

            $model->unlinkOldFile ( $oldFile );

            $transaction->commit ();
            return $this->redirect ( [ 'view' , 'id' => $model->id ] );
        } catch ( Exception $ex ) {
            $transaction->rollBack ();
        }
    }
    return $this->render ( 'update' , [
        'model' => $model ,
        ] );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM