{"id":14862284,"url":"https://github.com/ZouYouShun/ngxf-uploader","last_synced_at":"2025-09-18T15:31:04.322Z","repository":{"id":52431325,"uuid":"98645369","full_name":"ZouYouShun/ngxf-uploader","owner":"ZouYouShun","description":"File uploader for Angular 18+","archived":false,"fork":false,"pushed_at":"2025-01-20T16:37:30.000Z","size":359,"stargazers_count":28,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-20T14:06:26.353Z","etag":null,"topics":["angular","uploader"],"latest_commit_sha":null,"homepage":"https://stackblitz.com/edit/stackblitz-starters-3vs7s3?file=src%2Fmain.ts","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ZouYouShun.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-28T11:59:03.000Z","updated_at":"2025-04-08T07:44:11.000Z","dependencies_parsed_at":"2025-01-03T01:17:56.080Z","dependency_job_id":"2247dbb8-7667-4e8d-b30d-1df8e0759caa","html_url":"https://github.com/ZouYouShun/ngxf-uploader","commit_stats":{"total_commits":56,"total_committers":7,"mean_commits":8.0,"dds":0.6071428571428572,"last_synced_commit":"0172d9052e96dd9a89160ef97813745ef90d1814"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/ZouYouShun/ngxf-uploader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZouYouShun%2Fngxf-uploader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZouYouShun%2Fngxf-uploader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZouYouShun%2Fngxf-uploader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZouYouShun%2Fngxf-uploader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ZouYouShun","download_url":"https://codeload.github.com/ZouYouShun/ngxf-uploader/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZouYouShun%2Fngxf-uploader/sbom","scorecard":{"id":157361,"data":{"date":"2025-08-11","repo":{"name":"github.com/ZouYouShun/ngxf-uploader","commit":"c5c40127583190dbcbc1cbff7f3974b48459d34f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.3,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":2,"reason":"Found 4/16 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: MIT License: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 19 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T12:12:23.180Z","repository_id":52431325,"created_at":"2025-08-16T12:12:23.180Z","updated_at":"2025-08-16T12:12:23.180Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275789271,"owners_count":25528712,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-18T02:00:09.552Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","uploader"],"created_at":"2024-09-19T20:01:33.768Z","updated_at":"2025-09-18T15:31:04.014Z","avatar_url":"https://github.com/ZouYouShun.png","language":"TypeScript","funding_links":[],"categories":["Third Party Components"],"sub_categories":["File Upload"],"readme":"[![NPM version](https://badge.fury.io/js/ngxf-uploader.svg)](http://badge.fury.io/js/ngxf-uploader)\n\n# ngxf-uploader\n\nFile uploader for Angular 6+, just use Angular HttpClient, no other dependence. [GitHub](https://github.com/ZouYouShun/ngxf-uploader)\n\n- ✅ `file` upload\n- ✅ `multiple File` upload\n- ✅ `accept` support\n- ✅ `Progress` support\n- ✅ `upload http request` support\n- ✅ `folder` upload, thanks for [SHANG-TING](https://github.com/SHANG-TING), more detail about file upload with folder, can view his [blog](https://scullyio-blog.gofa.cloud/blog/recursive_file_uploader)\n\n## Stackblitz Example\n\n[Stackblitz](https://stackblitz.com/edit/stackblitz-starters-3vs7s3?file=src%2Fmain.ts)\n\n#### Future\n\n```json\n[\n  {\n    \"name\": \"folder name\",\n    \"files\": [...files],\n  }\n]\n```\n\n![](https://res.cloudinary.com/dw7ecdxlp/image/upload/v1532609570/ngxfuploader_w7lxqv.gif)\n\n## Description\n\nSelect file or Drop, Paste file, and return an Observable. You can custom your behavior use [RxJs 7.x](https://github.com/Reactive-Extensions/RxJS) .\n\nProvide an sample way for upload by custom options like header, params, fields, file's form name.\n\n## Example\n\n## Install\n\n```ts\nnpm install ngxf-uploader --save\n```\n\n- Import `HttpClientModule`, `NgxfUploaderModule` into your main AppModule or the module where you want use.\n\n```ts\nimport { provideHttpClient } from '@angular/common/http';\nimport { provideExperimentalZonelessChangeDetection, model, inject } from '@angular/core';\n\nimport { of, catchError, finalize, tap } from 'rxjs';\n\nimport { ChangeDetectionStrategy, Component } from '@angular/core';\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { FileError, NgxfDirectoryStructure, NgxfDropDirective, NgxfSelectDirective, NgxfUploaderService, UploadEvent, UploadStatus, NgxfParseDirective } from 'ngxf-uploader';\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  imports: [NgxfSelectDirective, NgxfDropDirective, NgxfParseDirective],\n  templateUrl: './app.component.html',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class App {\n  progress = 0;\n  isUploading = false;\n  upload = inject(NgxfUploaderService);\n\n  uploadFile(file: File | File[] | NgxfDirectoryStructure[] | FileError) {\n    console.log(file);\n    this.isUploading = true;\n\n    if (!(file instanceof File) || Array.isArray(file)) {\n      // this.alertError(file);\n      this.isUploading = false;\n      return;\n    }\n\n    this.upload\n      .upload({\n        url: 'your api url',\n        headers: {\n          Authorization: 'token',\n        }, // Option\n        params: {\n          test: '123',\n        }, // Option\n        fields: {\n          // Option\n          toUrl: 'device',\n        },\n        filesKey: 'fileKey', // Option\n        files: file,\n        process: true,\n      })\n      .pipe(\n        tap((event: UploadEvent) =\u003e {\n          console.log(event);\n          this.progress = event.percent || 0;\n\n          if (event.status === UploadStatus.Completed) {\n            alert(`This file upload success!`);\n          }\n        }),\n        catchError((err) =\u003e {\n          console.error(err);\n          // alert(`upload fail`);\n          return of(null);\n        }),\n        finalize(() =\u003e {\n          console.log('end');\n        }),\n      )\n      .subscribe();\n  }\n\n  // Do something you want when file error occur.\n  alertError(msg: FileError) {\n    switch (msg) {\n      case FileError.NumError:\n        alert('Number Error');\n        break;\n      case FileError.SizeError:\n        alert('Size Error');\n        break;\n      case FileError.TypeError:\n        alert('Type Error');\n        break;\n    }\n  }\n}\n\nbootstrapApplication(App, {\n  providers: [provideHttpClient(), provideExperimentalZonelessChangeDetection()],\n});\n```\n\n- Add directive in the template where you want to use.\n\n```html\n\u003c!-- select file --\u003e\n\u003cbutton (ngxf-select)=\"uploadFile($event)\"\u003eUpload Single File\u003c/button\u003e\n\n\u003c!-- drop file \u0026 parse image --\u003e\n\u003cdiv (ngxf-drop)=\"uploadFiles($event)\" (ngxf-parse)=\"uploadFiles($event)\" [ngxf-validate]=\"{size: {min: 5000, max:2566621}, skipInvalid: true}\" drop-class=\"drop\" accept=\"image/*\" multiple\u003e\n  \u003ch3\u003eDrop file and parse image into here or click here to choice file.\u003c/h3\u003e\n\u003c/div\u003e\n```\n\n- Add `NgxfUploaderService` in the constructor and create file upload method in the typescript and upload file to server.\n\n```ts\nimport { Component } from '@angular/core';\nimport { FileError, NgxfUploaderService, UploadEvent, UploadStatus } from 'ngxf-uploader';\n\n@Component({\n  selector: 'app-drop-file',\n  templateUrl: './drop-file.component.html',\n  styleUrls: ['./drop-file.component.scss'],\n})\nexport class DropFileComponent {\n  progress = 0;\n  isUploading = false;\n\n  constructor(private Upload: NgxfUploaderService) {}\n\n  uploadFile(file: File | FileError): void {\n    console.log(file);\n    this.isUploading = true;\n    if (!(file instanceof File)) {\n      this.alertError(file);\n      this.isUploading = false;\n      return;\n    }\n    this.Upload.upload({\n      url: 'your api url',\n      headers: {\n        Authorization: 'token',\n      }, // Option\n      params: {\n        test: '123',\n      }, // Option\n      fields: {\n        // Option\n        toUrl: 'device',\n      },\n      filesKey: 'fileKey', // Option\n      files: file,\n      process: true,\n    }).subscribe(\n      (event: UploadEvent) =\u003e {\n        console.log(event);\n        this.progress = event.percent;\n        if (event.status === UploadStatus.Completed) {\n          alert(`This file upload success!`);\n        }\n      },\n      (err) =\u003e {\n        console.log(err);\n      },\n      () =\u003e {\n        this.isUploading = false;\n        console.log('complete');\n      },\n    );\n  }\n\n  uploadFiles(files: File[] | FileError): void {\n    console.log(files);\n    this.isUploading = true;\n    if (!(files instanceof Array)) {\n      this.alertError(files);\n      this.isUploading = false;\n      return;\n    }\n    this.Upload.upload({\n      url: 'your api url',\n      headers: {\n        Authorization: 'token',\n      }, // Option\n      params: {\n        test: '123',\n      }, // Option\n      fields: {\n        // Option\n        toUrl: 'device',\n      },\n      filesKey: 'fileKey', // Option\n      files: files,\n      process: true, // if you want process event, set process true\n    }).subscribe(\n      (event: UploadEvent) =\u003e {\n        console.log(event);\n        this.progress = event.percent;\n        if (event.status === UploadStatus.Completed) {\n          alert(`upload complete!`);\n        }\n      },\n      (err) =\u003e {\n        console.log(err);\n      },\n      () =\u003e {\n        this.isUploading = false;\n        console.log('complete');\n      },\n    );\n  }\n\n  // Do something you want when file error occur.\n  alertError(msg: FileError) {\n    switch (msg) {\n      case FileError.NumError:\n        alert('Number Error');\n        break;\n      case FileError.SizeError:\n        alert('Size Error');\n        break;\n      case FileError.TypeError:\n        alert('Type Error');\n        break;\n    }\n  }\n}\n```\n\n## API\n\n## Attribute Detail\n\n| Attribute         | necessary(default) | type                           | position                               | description                                                                           |\n| ----------------- | ------------------ | ------------------------------ | -------------------------------------- | ------------------------------------------------------------------------------------- |\n| `(ngxf-select)`   | yes                | `(Array)=\u003eFile or FileError`   | any tag                                | provide a directive that can let you select file upload by click                      |\n| `(ngxf-drop)`     | yes                | `(Array)=\u003eFile[] or FileError` | any tag                                | provide a directive for you to set area can be drop file into                         |\n| `(ngxf-parse)`    | yes                | `(Array)=\u003eFile[] or FileError` | any tag                                | provide a directive for you to set area can be parse file into                        |\n| `[ngxf-validate]` | no                 | `FileOption`                   | with `(ngxf-drop)` and `(ngxf-select)` | file validate with file size, and other options                                       |\n| `[drop-class]`    | no('drop')         | `string`                       | with `(ngxf-drop)` and `(ngxf-select)` | when drop on tag, this class will append on it                                        |\n| `[accept]`        | no                 | `string`                       | with `(ngxf-drop)` and `(ngxf-select)` | accept file type                                                                      |\n| `[multiple]`      | no                 | `boolean`                      | with `(ngxf-drop)` and `(ngxf-select)` | is allow multiple file                                                                |\n| `[folder]`        | no                 | `boolean`                      | `(ngxf-select)`                        | is allow select folder file                                                           |\n| `[structure]`     | no                 | `boolean`                      | `(ngxf-drop)`                          | show the structure of all folders and files with the new feature of dragging folders. |\n\n### Service Upload Method\n\nThis method will return an `Observable\u003cUploadEvent\u003e`, that you can subscribe it, and return a UploadEvent.\n\n```ts\nupload(d: UploadObject): Observable\u003cUploadEvent\u003e;\n```\n\n### Upload Object\n\n```ts\nexport interface UploadObject {\n  /** target upload api url */\n  url: string;\n  /** upload file or files, also support Blob */\n  files: File | File[] | Blob | Blob[];\n  /**\n   * target file key name\n   *\n   * @default\n   * 'file'\n   */\n  filesKey?: string | string[];\n  /** is that need report upload progress,\n   *\n   * @default\n   * false\n   */\n  process?: boolean;\n  /** other fields that you want to attach together */\n  fields?: any;\n  /** other headers that you want to attach together */\n  headers?: { [name: string]: string | string[] } | HttpHeaders;\n  /** other params that you want to attach together */\n  params?: { [name: string]: string | string[] } | HttpParams;\n  /** response type */\n  responseType?: 'arraybuffer' | 'blob' | 'json' | 'text';\n  /**\n   * is that with credentials\n   *\n   * view more:\n   *\n   * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials\n   */\n  withCredentials?: boolean;\n  /**\n   * request api method type,\n   *\n   * @default\n   * POST\n   */\n  method?: string;\n}\n```\n\n### Return Object\n\nYou can use this object when event return.\n\n```ts\nexport interface UploadEvent {\n  /**\n   * upload status\n   *\n   * - `UploadStatus.Uploading`\n   * - `UploadStatus.Completed`\n   * - `UploadStatus.UploadError`\n   * - `UploadStatus.FileNumError`\n   */\n  status: UploadStatus;\n  /** what percent of current upload rate */\n  percent: number;\n  /** other data you want to attach */\n  data?: any;\n}\n```\n\n### FileOption\n\n```ts\nexport interface FileOption {\n  /**\n   * check upload file size\n   * unit: `Byte`\n   */\n  size?: {\n    /** the smallest bytes */\n    min?: number;\n    /** the biggest bytes */\n    max?: number;\n  }; // unit: Byte,\n  /**\n   * when you upload some files in once, but not throw error when have some file not in the range\n   * you can set it to true, let will skip the Invalid file\n   *\n   * @default false\n   */\n  skipInvalid?: boolean;\n}\n```\n\n### FileError\n\nYou can use this enum to conclude the file select return.\n\n```ts\nexport const enum FileError {\n  /** when number of file Error */\n  NumError,\n  /** when file accept type Error */\n  TypeError,\n  /** when file size error */\n  SizeError,\n}\n```\n\n### UploadStatus\n\nYou can use this enum to conclude the return Event.\n\n```ts\nexport const enum UploadStatus {\n  /** when file is uploading. */\n  Uploading,\n  /** when upload complete. */\n  Completed,\n  /** when server error. */\n  UploadError,\n  /** when no choice file. */\n  FileNumError,\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FZouYouShun%2Fngxf-uploader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FZouYouShun%2Fngxf-uploader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FZouYouShun%2Fngxf-uploader/lists"}