Yii2-无法上传图片

时间:2018-08-18 05:37:38

标签: php yii2 image-uploading yii2-advanced-app

我正在研究yii2。在我看来,我正在尝试上传一张图片。但是我无法上传。

模型

class MeterAcceptanceHeader extends \yii\db\ActiveRecord
{

public static $status_titles =[
    0 => 'Prepared',
    1 => 'Created',
    2 => 'Printed',
    3 => 'Canceled',
];

/**
 * @inheritdoc
 */
public static function tableName()
{
    return 'meter_acceptance_header';
}

/**
 * @inheritdoc
 */
public function rules()
{
    return [
        [['sub_div', 'prepared_by'], 'required'],
        [['prepared_by', 'updated_by'], 'integer'],
        [['prepared_at', 'updated_at'], 'safe'],
        [['sub_div', 'meter_type', 'status'], 'string', 'max' => 100],
        [['images'], 'string', 'max' => 255],
        [['images'], 'file', 'skipOnEmpty' => true, 'extensions' => 'png,jpg,pdf', 'maxFiles' => 4],
        [['sub_div'], 'exist', 'skipOnError' => true, 'targetClass' => SurveyHescoSubdivision::className(), 'targetAttribute' => ['sub_div' => 'sub_div_code']],
        [['prepared_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['prepared_by' => 'id']],
    ];
}

/**
 * @inheritdoc
 */
public function attributeLabels()
{
    return [
        'id' => 'ID',
        'sub_div' => 'Sub Div',
        'meter_type' => 'Meter Type',
        'prepared_by' => 'Prepared By',
        'prepared_at' => 'Prepared At',
        'updated_at' => 'Updated At',
        'status' => 'Status',
        'updated_by' => 'Updated By',
        'images' => 'Document Snap',
    ];
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getMeterAcceptanceDetails()
{
    return $this->hasMany(MeterAcceptanceDetails::className(), ['accpt_id' => 'id']);
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getSubDiv()
{
    return $this->hasOne(SurveyHescoSubdivision::className(), ['sub_div_code' => 'sub_div']);
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getPrepared()
{
    return $this->hasOne(User::className(), ['id' => 'prepared_by']);
}
}

MeterAcceptanceHeader表

enter image description here

MeterAcceptanceImages表

enter image description here

有一个form1,提示用户从下拉菜单中进行选择。

Form1视图

<div class="meter-acceptance-header-form">
<?php $model->status = common\models\MeterAcceptanceHeader::$status_titles[0]; ?>

<?php $form = ActiveForm::begin(['id'=>'acceptance-form','options' => ['enctype' => 'multipart/form-data']]); ?>

<?= $form->field($model, 'sub_div')->dropDownList([''=>'Please Select'] + \common\models\SurveyHescoSubdivision::toArrayList()) ?>

<?= $form->field($model, 'meter_type')->dropDownList([''=>'Please Select','Single-Phase' => 'Single-Phase', '3-Phase' => '3-Phase', 'L.T.TOU' => 'L.T.TOU']) ?>

<?= $form->field($model, 'status')->textInput(['maxlength' => true,'readonly' => true]) ?>

<div class="form-group">
    <a class="btn btn-default" onclick="window.history.back()" href="javascript:;"><i
                class="fa fa-close"></i>
        Cancel</a>
    <a class="<?= $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ?>" onclick="
      $('#acceptance-form').submit();" href="javascript:">
        <?= $model->isNewRecord ? 'Create' : 'Update' ?></a>
</div>

<?php ActiveForm::end(); ?>

点击Create按钮后,将提示用户进入第二种形式,用户将在其中以一种形式上传图像。

下面是我要从中上传控制器的代码。

public function actionSetpdf($id)
{
    $model = $this->findModel($id);
    $m = 0;
    $accpt_id = $model->id;
    $meter_type = $model->meter_type;
    $ogp_sub_div = $model->sub_div;
    $images=[];
    $ic=0;
    $files_uploaded = false;
    if(Yii::$app->request->isAjax && Yii::$app->request->post())
    {
        $data = explode(',',$_POST['data']);

        foreach($data as $value)
        {

            $m = new MeterAcceptanceDetails;
            $m -> load(Yii::$app->request->post());

            $m->accpt_id = $accpt_id;
            $m->meter_type = $meter_type;
            $m->created_at = date('Y-m-d H:i:s');
            $m->created_by = Yii::$app->user->id;
            $m->meter_id = $value;
            $m->meter_msn = \common\models\Meters::idTomsn($value);
            $m->flag = 1;// 1 means created
            $m->ogp_sub_div = $ogp_sub_div;

            if($m->save())
            { 
                // Here the upload image code starts
                if($ic==0)
                {

                    $model->images = UploadedFile::getInstances($model, 'images');
                    foreach ($model->images as $file)
                    {
                        if (file_exists($file->tempName))
                        {
                            $img_s = new MeterAcceptanceImages;
                            $file_name = rand(0, 1000) . time().date('his') . '.' . $file->extension;
                            $file->saveAs('uploads/meter_acceptance/' . $file_name);
                            $img_s->file_path = $file_name;
                            $img_s->accpt_id = $accpt_id;

                            if ($img_s->save()) {
                                $images[] = $img_s;

                            } else {
                                print_r($img_s->getErrors());
                            }
                        }
                    }
                }else{
                    foreach($images as $image){
                        $img_s = new MeterAcceptanceImages;
                        $img_s->file_path = $image->file_path;
                        $img_s->accpt_id = $accpt_id;
                        $img_s->save();
                    }
                }

                $model->status = MeterAcceptanceHeader::$status_titles[1];
                $model->update();

            }
            else{

                $this->renderAjax('viewcreated');
            }
        }

    }
    else{
        $this->renderAjax('viewcreated');
    }


    return $this->redirect(Url::toRoute(['meteracceptanceheader/viewsetpdf','id' => $model->id,'model' => $this->findModel($id)]));

}

Form2视图

<div class="map-meters-form" id="doc">
<?php $form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false,
'options' => ['enctype' => 'multipart/form-data']]) ?>
<section class="content">
<div class="box">
    <div id="chk" class="box-body">
        <?php Pjax::begin(); ?>
        <?= DetailView::widget([
            'model' => $model,
            'attributes' => [

                [
                        'label'=>'Serial #',
                         'value' => function($d)
                         {
                             return $d->id;
                         }
                ],
                [
                    'label' => 'Meter Type',
                    'value' => function ($d) {
                        if(is_object($d))
                            return $d->meter_type;
                        return ' - ';
                    },


                ],
                'sub_div',
                [
                    'label' => 'Sub Division Name',
                    'value' => function ($d) {
                        if(is_object($d))
                            return $d->subDiv->name;
                        return '-';
                    },


                ],
                [
                    'label' => 'Prepared By',
                    'value' => function ($d) {
                        if(is_object($d))
                            return $d->prepared->name;
                    },


                ],
                'prepared_at',

                'status',


            ],
        ]) ?>
        <br>
        <div class="pre-scrollable">
        <?= GridView::widget([
            'dataProvider' => $dataProvider,
            //'ajaxUpdate'       => true,
            'filterModel' => false,
            //'id'=>'gv',

            'columns' => [
                ['class' => 'yii\grid\SerialColumn'],
                ['class' => 'yii\grid\CheckboxColumn', 'checkboxOptions' => function($d) {
                    return ['value' => $d['meter_id']];
                }],
                'Meter_Serial_Number',
                'Meter_Type',
                'Sub_Division_Code',
                'Sub_Division_Name',
            ],
        ]); ?>
        </div>
        <?php Pjax::end(); ?>

        <?= $form->field($model, 'images[]')->fileInput(['multiple' => true, 'accept' => 'image/*'])?>
        <br>
        <form>
            <p>
                <a href="<?= URL::toRoute(['meteracceptanceheader/setpdf', 'id'=>$model->id])?>" name="redirect" class="btn btn-primary" id="myid">Submit</a>

                <br/>

            </p>
        </form>
    </div>
  </div>
  </section>
  <?php ActiveForm::end(); ?>
  </div>
  <?php
  $url = Url::toRoute(['/meteracceptanceheader/setpdf','id'=>$model->id]);
  $script = <<< JS
  $(document).ready(function () {  

  $(document).on('pjax:end', function() {
  $("#chk").find("input:checkbox").prop("checked", true);
  });
  $("#chk").find("input:checkbox").prop("checked", true);

  $('#myid').on('click',function(e) {


   e.preventDefault();    
   var strValue = "";        
    $('input[name="selection[]"]:checked').each(function() {

    if(strValue!=="")
        {
        strValue = strValue + " , " + this.value;

        }
    else 
       strValue = this.value;     

  });

  $.ajax({
     url: '$url',
     type: 'POST',
     dataType: 'json',
     data: {data:strValue},         
     success: function(data) {
        alert(data);
     }
  });
  }) 
  });
  JS;
  $this->registerJs($script, \yii\web\View::POS_END);
  ?>

这将允许选择图像。现在,当我尝试单击“提交”按钮时,出现以下错误

  

PHP通知'yii \ base \ ErrorException'并显示消息'Undefined offset:0'

     

在   E:\ xampp \ htdocs \ inventory-web \ vendor \ yiisoft \ yii2 \ db \ Command.php:330

通过调试控制器代码,我发现错误来自foreach ($model->images as $file),因为print_r($model->images)返回Array()为空。

通过print_r($model)我得到了

common\models\MeterAcceptanceHeader Object ( [_attributes:yii\db\BaseActiveRecord:private] => Array ( [id] => 1 [sub_div] => 37111 [meter_type] => L.T.TOU [prepared_by] => 12 [prepared_at] => 2018-08-20 12:41:27 [updated_at] => [status] => Prepared [updated_by] => [images] => Array ( ) ) [_oldAttributes:yii\db\BaseActiveRecord:private] => Array ( [id] => 1 [sub_div] => 37111 [meter_type] => L.T.TOU [prepared_by] => 12 [prepared_at] => 2018-08-20 12:41:27 [updated_at] => [status] => Prepared [updated_by] => [images] => ) [_related:yii\db\BaseActiveRecord:private] => Array ( ) [_errors:yii\base\Model:private] => [_validators:yii\base\Model:private] => [_scenario:yii\base\Model:private] => default [_events:yii\base\Component:private] => Array ( ) [_behaviors:yii\base\Component:private] => Array ( ) )

我在其他模块中也使用了相同的过程,因此可以正常工作。

我如何摆脱这个问题?

更新1

<?php

use yii\helpers\Html;
use yii\grid\GridView;
use yii\helpers\Url;
use app\models\User;
use yii\widgets\DetailView;
use yii\widgets\ActiveForm;
use yii\widgets\Pjax;
use kartik\select2\Select2;
use kartik\file\FileInput;


/* @var $this yii\web\View */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = $model->id;
$this->title = 'Meter Acceptance Form';
$this->params['breadcrumbs'][] = $this->title;
?>
<section class="content-header">
   <h1>Meter Acceptance</h1>
</section>
<div class="map-meters-form" id="doc">

<section class="content">
    <div class="box">
        <div id="chk" class="box-body">
            <?php Pjax::begin(); ?>
            <?=
            DetailView::widget([
                'model' => $model,
                'attributes' => [
                    [
                        'label' => 'Serial #',
                        'value' => function($d){
                            return $d->id;
                        }
                    ],
                    [
                        'label' => 'Meter Type',
                        'value' => function ($d){
                            if( is_object($d) )
                                return $d->meter_type;
                            return ' - ';
                        },
                    ],
                    'sub_div',
                    [
                        'label' => 'Sub Division Name',
                        'value' => function ($d){
                            if( is_object($d) )
                                return $d->subDiv->name;
                            return '-';
                        },
                    ],
                    [
                        'label' => 'Prepared By',
                        'value' => function ($d){
                            if( is_object($d) )
                                return $d->prepared->name;
                        },
                    ],
                    'prepared_at',
                    'status',
                ],
            ])
            ?>
            <br>
            <div class="pre-scrollable">
                <?=
                GridView::widget([
                    'dataProvider' => $dataProvider,
                    //'ajaxUpdate'       => true,
                    'filterModel' => false,
                    //'id'=>'gv',
                    'columns' => [
                        ['class' => 'yii\grid\SerialColumn'],
                        ['class' => 'yii\grid\CheckboxColumn', 'checkboxOptions' => function($d){
                            return ['value' => $d['meter_id']];
                        }],
                        'Meter_Serial_Number',
                        'Meter_Type',
                        'Sub_Division_Code',
                        'Sub_Division_Name',
                    ],
                ]);
                ?>
            </div>
            <?php Pjax::end(); ?>
            <?php
            $form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false,
                'options' => ['enctype' => 'multipart/form-data']])
            ?>
            <?=$form->field($model, 'images[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>
            <br>
            <p>
                <a href="<?=URL::toRoute(['meteracceptanceheader/setpdf', 'id' => $model->id]) ?>" name="redirect" class="btn btn-primary" id="myid">Submit</a>

                <br/>

            </p>
            <?php ActiveForm::end(); ?>
        </div>
    </div>
</section>
</div>
<?php
$url = Url::toRoute(['/meteracceptanceheader/setpdf','id'=>$model->id]);
$script = <<< JS
$(document).ready(function () {  

$(document).on('pjax:end', function() {
       $("#chk").find("input:checkbox").prop("checked", true);
});
      $("#chk").find("input:checkbox").prop("checked", true);

   $('#myid').on('click',function(e) {

  e.preventDefault();    

 //START Append form data
  var data = new FormData();

  var files= $('input[name="MeterAcceptanceHeader[images][]"]')[0].files;

  //append files
  $.each(files,function(index,file){
      data.append("MeterAcceptanceHeader[images][]",file,file.name);
  });


 var strValue = "";        
    $('input[name="selection[]"]:checked').each(function() {

    if(strValue!=="")
        {
        strValue = strValue + " , " + this.value;

        }
    else 
       strValue = this.value;     

});
    //alert(strValue);

    //append your query string to the form data too
  data.append('data',strValue);

  //END append form data
  $.ajax({
     url: '$url',
     type: 'POST',
     dataType: 'json',
      contentType: false,
      processData: false,
     data: {data:strValue},         
     success: function(data) {
        alert(data);
     }
     });

     }) 
     });
     JS;
     $this->registerJs($script, \yii\web\View::POS_END);
     ?>

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:8)

当您将click绑定到#myid<a href="<?= URL::toRoute(['meteracceptanceheader/setpdf', 'id'=>$model->id])?>" name="redirect" class="btn btn-primary" id="myid">Submit</a> 上时,当您在步骤2中单击“提交”按钮以上传图像时,您试图通过Ajax调用提交图像。定位按钮

FormData

如果您尝试通过ajax发送图像,则需要使用Form2界面。

  

FormData接口提供了一种轻松构造一组   表示表单字段及其值的键/值对,可以   然后可以使用XMLHttpRequest.send()方法轻松发送。它用   如果将编码类型设置为,则表单将使用相同的格式   “ multipart / form-data”。

但是在我介绍如何做之前,您需要研究与<?php $form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false, 'options' => ['enctype' => 'multipart/form-data']]) ?> 视图相关的其他问题。

  • 您正在嵌套这两种形式,这在技术上是错误的,您无法这样做,请参见Why

  • 首先,为什么要为“提交”按钮创建单独的表单?

    请首先查看此行

    <?= $form->field($model, 'images[]')->fileInput(['multiple' => true, 'accept' => 'image/*'])?>

    这是您的第一个表格开始的地方,并且是文件输入字段

    ActiveForm

    位于此表单中,在下一行中,您具有上面刚提到的定位按钮,但是您将其包装在单独的表单中,并且在关闭 <form> <p> <a href="<?= URL::toRoute(['meteracceptanceheader/setpdf', 'id'=>$model->id])?>" name="redirect" class="btn btn-primary" id="myid">Submit</a> <br/> </p> </form>

    ActiveForm

    您将通过调用<?php ActiveForm::end(); ?>关闭此表单后的GridVew。正如您在上一个question中所见,在将GridView包装在表单内时,您遇到了ActiveForm的过滤器输入的怪异行为,并且在这里重复了同样的错误并在其中嵌套2种形式。

我将建议您先做什么

  • 删除仅为锚按钮创建的表单,因为如果您想通过单击锚通过ajax提交图像,则不需要它,只需将锚保留在主ActiveForm::begin()中即可。然后将fileInput()移动到Pjax::end()之前和FormData之后。

话虽如此,您现在应该使用contentType: false来通过ajax上传图像,然后您必须在ajax调用中添加这些选项processData: falseFormData.append()并使用{{ 1}},将输入文件附加到FormData

因此,用于点击功能的javascript如下所示,我假设用于图片上传的模型为MeterAcceptanceImages

$('#myid').on('click',function(e) {
      event.preventDefault();

      //START Append form data
      let data = new FormData();

      let files= $("input[name='MeterAcceptanceImages[images][]']")[0].files;

      //append files
      $.each(files,function(index,file){
          data.append('MeterAcceptanceImages[images][]',file,file.name);
      });

      var strValue = "";
      $('input[name="selection[]"]:checked').each(function() {
           if(strValue!==""){
               strValue = strValue + " , " + this.value;
           }else{
              strValue = this.value;     
           }
      });

      //append your query string to the form data too
      data.append('data',strValue);

      //END append form data

      $.ajax({
          url: '$url',
          type: 'POST',
          dataType: 'json',
          contentType: false,
          processData: false,
          data: data,
          success: function(data) {
             alert(data);
          }
      });
});

因此,总体而言,您的视图Form2.php应该如下所示

<div class="map-meters-form" id="doc">

    <section class="content">
        <div class="box">
            <div id="chk" class="box-body">
                <?php Pjax::begin(); ?>
                <?=
                DetailView::widget([
                    'model' => $model,
                    'attributes' => [
                        [
                            'label' => 'Serial #',
                            'value' => function($d){
                                return $d->id;
                            }
                        ],
                        [
                            'label' => 'Meter Type',
                            'value' => function ($d){
                                if( is_object($d) )
                                    return $d->meter_type;
                                return ' - ';
                            },
                        ],
                        'sub_div',
                        [
                            'label' => 'Sub Division Name',
                            'value' => function ($d){
                                if( is_object($d) )
                                    return $d->subDiv->name;
                                return '-';
                            },
                        ],
                        [
                            'label' => 'Prepared By',
                            'value' => function ($d){
                                if( is_object($d) )
                                    return $d->prepared->name;
                            },
                        ],
                        'prepared_at',
                        'status',
                    ],
                ])
                ?>
                <br>
                <div class="pre-scrollable">
                    <?=
                    GridView::widget([
                        'dataProvider' => $dataProvider,
                        //'ajaxUpdate'       => true,
                        'filterModel' => false,
                        //'id'=>'gv',
                        'columns' => [
                            ['class' => 'yii\grid\SerialColumn'],
                            ['class' => 'yii\grid\CheckboxColumn', 'checkboxOptions' => function($d){
                                    return ['value' => $d['meter_id']];
                                }],
                            'Meter_Serial_Number',
                            'Meter_Type',
                            'Sub_Division_Code',
                            'Sub_Division_Name',
                        ],
                    ]);
                    ?>
                </div>
                <?php Pjax::end(); ?>
                <?php
                $form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false,
                            'options' => ['enctype' => 'multipart/form-data']])
                ?>
                <?=$form->field($model, 'images[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>
                <br>
                <p>
                    <a href="<?=URL::toRoute(['meteracceptanceheader/setpdf', 'id' => $model->id]) ?>" name="redirect" class="btn btn-primary" id="myid">Submit</a>

                    <br/>

                </p>
                <?php ActiveForm::end(); ?>
            </div>
        </div>
    </section>
</div>
<?php
$url = Url::toRoute(['/meteracceptanceheader/setpdf', 'id' => $model->id]);
$script = <<< JS
  $(document).ready(function () {  

  $(document).on('pjax:end', function() {
    $("#chk").find("input:checkbox").prop("checked", true);
  });
  $("#chk").find("input:checkbox").prop("checked", true);

$('#myid').on('click',function(e) {
      event.preventDefault();

      //START Append form data
      let data = new FormData();

      let files= $("input[name='MeterAcceptanceImages[images][]']")[0].files;

      //append files
      $.each(files,function(index,file){
          data.append('MeterAcceptanceImages[images][]',file,file.name);
      });

      var strValue = "";
      $('input[name="selection[]"]:checked').each(function() {
           if(strValue!==""){
               strValue = strValue + " , " + this.value;
           }else{
              strValue = this.value;     
           }
      });

      //append your query string to the form data too
      data.append('data',strValue);

      //END append form data

      $.ajax({
          url: '$url',
          type: 'POST',
          dataType: 'json',
          contentType: false,
          processData: false,
          data: data,
          success: function(data) {
             alert(data);
          }
      });
});

  });
JS;
$this->registerJs($script, \yii\web\View::POS_END);
?>

现在,如果您尝试print_r(UploadedFile::getInstances('images')),应该会向您显示您选择并提交的所有图像。要在上载ajax调用时出错时进行故障排除,您可以查看我之前发布的answer与ajax文件上传相关的内容。