从 <input type="file"> 上传文件

Posted

技术标签:

【中文标题】从 <input type="file"> 上传文件【英文标题】:File upload from <input type="file"> 【发布时间】:2016-05-25 18:54:15 【问题描述】:

使用 angular 2 beta,我似乎无法让 &lt;input type="file"&gt; 工作。

使用诊断,我可以看到其他types 的双向绑定,例如text

<form>
    diagnostic
    <div class="form-group">
        <label for="fileupload">Upload</label>
        <input type="file" class="form-control" [(ngModel)]="model.fileupload">
    </div>
</form>

在我的 TypeScript 文件中,我有以下诊断行:

get diagnostic()  return JSON.stringify(this.model); 

会不会是不是 JSON 的问题?值为null

我无法真正验证input 的值。 У尽管“选择文件...”旁边的文本更新了,但由于某种原因,我看不到 DOM 中的差异。

【问题讨论】:

我是用这种方式上传文件的。看看有没有帮助:***.com/a/43288706/3764156 【参考方案1】:

我认为它不受支持。如果您查看此DefaultValueAccessor 指令(请参阅https://github.com/angular/angular/blob/master/modules/angular2/src/common/forms/directives/default_value_accessor.ts#L23)。您将看到用于更新绑定元素的值是$event.target.value

这不适用于file 类型的输入,因为文件对象可以改为$event.srcElement.files

更多详情,你可以看看这个plunkr:https://plnkr.co/edit/ozZqbxIorjQW15BrDFrg?p=info:

@Component(
  selector: 'my-app',
  template: `
    <div>
      <input type="file" (change)="onChange($event)"/>
    </div>
  `,
  providers: [ UploadService ]
)
export class AppComponent 
  onChange(event) 
    var files = event.srcElement.files;
    console.log(files);
  

【讨论】:

我认为客户端无法为该值生成 JSON 值可能是设计使然... 事实上,ngModel 可以用于生成 JSON 以外的其他用途;-) 也许使用files 属性来设置绑定ngModel 的值可能是可能的。 . 我的 2 美分 onChange 事件无法识别是否第二次上传相同的文件,数据发生变化。 这似乎无效,event 没有srcElement 属性 请参阅this answer 了解 htmlInputEvent 类型。【参考方案2】:
@Component(
  selector: 'my-app',
  template: `
    <div>
      <input name="file" type="file" (change)="onChange($event)"/>
    </div>
  `,
  providers: [ UploadService ]
)
export class AppComponent 
  file: File;
  onChange(event: EventTarget) 
        let eventObj: MSInputMethodContext = <MSInputMethodContext> event;
        let target: HTMLInputElement = <HTMLInputElement> eventObj.target;
        let files: FileList = target.files;
        this.file = files[0];
        console.log(this.file);
    

   doAnythingWithFile() 
   


【讨论】:

【参考方案3】:

有一种更好的方法来访问附件。您可以使用template reference variable 来获取输入元素的实例。

这是基于第一个答案的示例:

@Component(
  selector: 'my-app',
  template: `
    <div>
      <input type="file" #file (change)="onChange(file.files)"/>
    </div>
  `,
  providers: [ UploadService ]
)

export class AppComponent 
  onChange(files) 
    console.log(files);
  

这里有一个example app 来演示这一点。

模板引用变量可能有用,例如您可以直接在控制器中通过@ViewChild 访问它们。

【讨论】:

嗨@Belter,这是我刚刚制作的一个工作示例plnkr。您使用的是哪个版本的 Ng?究竟是什么不起作用? 嗯,我用 Ng 2.4.0 和 4.3.0 都试过了(看来你正在使用)。您也可以在文档中查看template reference variables。你有任何错误吗?【参考方案4】:

试试这个小库,适用于 Angular 5.0.0

https://www.npmjs.com/package/ng2-file-upload

ng2-file-upload 1.3.0 的快速入门示例:

用户点击自定义按钮,从隐藏的 input type="file" 触发上传对话框,选择单个文件后自动开始上传。

app.module.ts:

import FileUploadModule from "ng2-file-upload";

your.component.html:

...
  <button mat-button onclick="document.getElementById('myFileInputField').click()" >
    Select and upload file
  </button>
  <input type="file" id="myFileInputField" ng2FileSelect [uploader]="uploader" style="display:none">
...

your.component.ts:

import FileUploader from 'ng2-file-upload';
...    
uploader: FileUploader;
...
constructor() 
   this.uploader = new FileUploader(url: "/your-api/some-endpoint");
   this.uploader.onErrorItem = item => 
       console.error("Failed to upload");
       this.clearUploadField();
   ;
   this.uploader.onCompleteItem = (item, response) => 
       console.info("Successfully uploaded");
       this.clearUploadField();

       // (Optional) Parsing of response
       let responseObject = JSON.parse(response) as MyCustomClass;

   ;

   // Asks uploader to start upload file automatically after selecting file
  this.uploader.onAfterAddingFile = fileItem => this.uploader.uploadAll();


private clearUploadField(): void 
    (<HTMLInputElement>window.document.getElementById('myFileInputField'))
    .value = "";

替代库,适用于 Angular 4.2.4,但需要一些变通方法才能采用 Angular 5.0.0

https://www.npmjs.com/package/angular2-http-file-upload

【讨论】:

【参考方案5】:

Frelseren 提出的另一种使用模板引用变量和 ViewChild 的方法:

import  ViewChild  from '@angular/core';

@Component(
  selector: 'my-app',
  template: `
    <div>
      <input type="file" #fileInput/>
    </div>
  `
)  
export class AppComponent 
  @ViewChild("fileInput") fileInputVariable: any;
  randomMethod() 
    const files = this.fileInputVariable.nativeElement.files;
    console.log(files);
  

另见https://***.com/a/40165524/4361955

【讨论】:

【参考方案6】:

如果您有一个包含多个文件和其他输入的复杂表单,这里的解决方案很适合 ngModel

它由一个文件输入组件组成,该组件包装了一个简单的文件输入,并实现了ControlValueAccessor 接口以使其可以被ngModel 使用。该组件将FileList 对象公开给ngModel

此解决方案基于this 文章。

组件是这样使用的:

<file-input name="file" inputId="file" [(ngModel)]="user.photo"></file-input>
<label for="file"> Select file </label>

这是组件代码:

import  Component, Input, forwardRef  from '@angular/core';
import  NG_VALUE_ACCESSOR, ControlValueAccessor  from '@angular/forms';

const noop = () => 
;

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = 
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => FileInputComponent),
    multi: true
;

@Component(
  selector: 'file-input',
  templateUrl: './file-input.component.html',
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
)
export class FileInputComponent 

  @Input()
  public name:string;

  @Input()
  public inputId:string;

  private innerValue:any;

  constructor()  

  get value(): FileList 
    return this.innerValue;
  ;

  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (_: FileList) => void = noop;

  set value(v: FileList) 
    if (v !== this.innerValue) 
      this.innerValue = v;
      this.onChangeCallback(v);
    
  

  onBlur() 
    this.onTouchedCallback();
  

  writeValue(value: FileList) 
    if (value !== this.innerValue) 
      this.innerValue = value;
    
  

  registerOnChange(fn: any) 
    this.onChangeCallback = fn;
  

  registerOnTouched(fn: any) 
    this.onTouchedCallback = fn;
  

  changeFile(event) 
    this.value = event.target.files;
  

这是组件模板:

<input type="file" name=" name " id=" inputId " multiple="multiple" (change)="changeFile($event)"/>

【讨论】:

【参考方案7】:

试试(onclick)="this.value = null"

在您的 html 页面中添加 onclick 方法以删除以前的值,以便用户可以再次选择相同的文件。

【讨论】:

以上是关于从 <input type="file"> 上传文件的主要内容,如果未能解决你的问题,请参考以下文章

防止 ESC 从 Chrome 上的 <input type="search"> 清除文本(或添加按钮以清除文本而不使用 type="search"

如何调用 JS 函数,从 AUTOCOMPLETE 列表中选择 <input type="text"> 中的数据?

从 android 设备查看时更改网站中 <input type="file" accept="image/*"> 的上传选项

将 <input type="date"> 更改为波斯日期

input type="submit" 和"button"有什么区别

wdatepicker 日期空间 <input type="text"> 时间改变触发事件不好使怎么办