{"id":22087780,"url":"https://github.com/rainxh11/sakontstack.reactivestream","last_synced_at":"2026-05-04T04:33:21.447Z","repository":{"id":189367845,"uuid":"680550603","full_name":"rainxh11/SakontStack.ReactiveStream","owner":"rainxh11","description":"a Stream wrapper that provides read / write progress reporting through IProgress\u003cStreamProgress\u003e and an IObservable\u003cStreamProgress\u003e","archived":false,"fork":false,"pushed_at":"2023-08-19T20:22:42.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-31T08:42:49.429Z","etag":null,"topics":["csharp","dotnet","limit","observable","progress","reactive","reactiveextensions","stream","throttle"],"latest_commit_sha":null,"homepage":"","language":"C#","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/rainxh11.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2023-08-19T15:58:35.000Z","updated_at":"2024-11-12T14:48:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"78fda60e-18a1-4935-a2dc-3324d0afa35f","html_url":"https://github.com/rainxh11/SakontStack.ReactiveStream","commit_stats":null,"previous_names":["rainxh11/sakontstack.reactivestream"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rainxh11/SakontStack.ReactiveStream","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainxh11%2FSakontStack.ReactiveStream","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainxh11%2FSakontStack.ReactiveStream/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainxh11%2FSakontStack.ReactiveStream/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainxh11%2FSakontStack.ReactiveStream/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rainxh11","download_url":"https://codeload.github.com/rainxh11/SakontStack.ReactiveStream/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainxh11%2FSakontStack.ReactiveStream/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32595197,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T22:12:39.696Z","status":"online","status_checked_at":"2026-05-04T02:00:06.625Z","response_time":58,"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":["csharp","dotnet","limit","observable","progress","reactive","reactiveextensions","stream","throttle"],"created_at":"2024-12-01T02:06:38.362Z","updated_at":"2026-05-04T04:33:21.442Z","avatar_url":"https://github.com/rainxh11.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Reactive Stream\n\na `Stream` wrapper that provides Read/Write progress reporting through `IProgress\u003cStreamProgress\u003e` and `IObservable\u003cStreamProgress\u003e`\nwith speed throttling for Read/Write streams separately.\n\n| Version                                                                                                                                        | Downloads                                                                    |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |\n| [![Latest version](https://img.shields.io/nuget/v/SakontStack.ReactiveStream.svg)](https://www.nuget.org/packages/SakontStack.ReactiveStream/) | ![Downloads](https://img.shields.io/nuget/dt/SakontStack.ReactiveStream.svg) |\n\n## Example Usage:\n\n### Report file download progress:\n\n- progress using `IProgress\u003cStreamProgress\u003e`\n\n```csharp\nusing System.Reactive.Linq;\nusing SakontStack.ReactiveStream;\nusing ByteSizeLib;\n\nvar fileLink = new Uri(@\"https://releases.ubuntu.com/jammy/ubuntu-22.04.3-desktop-amd64.iso\");\n\nvar client   = new HttpClient();\nvar response = await client.GetAsync(fileLink, HttpCompletionOption.ResponseHeadersRead);\nvar length   = long.Parse(response.Content.Headers\n                                  .First(h =\u003e h.Key.Equals(\"Content-Length\"))\n                                  .Value\n                                  .First());\n\nvar progress = new Progress\u003cReactiveStream.StreamProgress\u003e(p =\u003e Console\n                                                              .WriteLine($\"Downloaded {ByteSize.FromBytes(p.Bytes).ToBinaryString()}\" +\n                                                                         $\"/{ByteSize.FromBytes(p.TotalBytes ?? 0).ToBinaryString()} \" +\n                                                                         $\"({p.Percentage:N2}%) \" +\n                                                                         $\"{ByteSize.FromBytes(p.BytesPerSecond).ToBinaryString()}/sec\"));\n\nvar stream = new ReactiveStream(new MemoryStream(), progress: progress,  totalLength: length);\n// stream progress will only start reporting progress after you subscribe to it\nstream.Subscribe();\nawait response.Content.CopyToAsync(stream);\n```\n\n_**NOTE:**_ _setting custom report interval while using `IProgress\u003cStreamProgress\u003e` alone, requires using `stream.Sample()` operator._\n\n- progress using `IObservable\u003cStreamProgress\u003e`\n\n```csharp\nusing System.Reactive.Linq;\nusing SakontStack.ReactiveStream;\nusing ByteSizeLib;\n\nvar fileLink = new Uri(@\"https://releases.ubuntu.com/jammy/ubuntu-22.04.3-desktop-amd64.iso\");\n\nvar client   = new HttpClient();\nvar response = await client.GetAsync(fileLink, HttpCompletionOption.ResponseHeadersRead);\nvar length   = long.Parse(response.Content.Headers\n                                  .First(h =\u003e h.Key.Equals(\"Content-Length\"))\n                                  .Value\n                                  .First());\n\nvar stream = new ReactiveStream(new MemoryStream(), totalLength: length);\nstream\n  .Sample(TimeSpan.FromSeconds(0.25)) // Only report progress each 0.25 seconds\n  .Do(p =\u003e Console\n            .WriteLine($\"Downloaded {ByteSize.FromBytes(p.Bytes).ToBinaryString()}\"+\n                       $\"/{ByteSize.FromBytes(p.TotalBytes ?? 0).ToBinaryString()}\"+\n                       $\"({p.Percentage:N2}%) \"+\n                       $\"{ByteSize.FromBytes(p.BytesPerSecond).ToBinaryString()}/sec\"))\n  .Subscribe();\nawait response.Content.CopyToAsync(stream);\n```\n\n- setting download speed limits:\n\n```csharp\nusing System.Reactive.Linq;\nusing SakontStack.ReactiveStream;\nusing ByteSizeLib;\n\nvar fileLink = new Uri(@\"https://releases.ubuntu.com/jammy/ubuntu-22.04.3-desktop-amd64.iso\");\n\nvar client   = new HttpClient();\nvar response = await client.GetAsync(fileLink, HttpCompletionOption.ResponseHeadersRead);\nvar length   = long.Parse(response.Content.Headers\n                                  .First(h =\u003e h.Key.Equals(\"Content-Length\"))\n                                  .Value\n                                  .First());\n\nvar stream = new ReactiveStream(new MemoryStream(), totalLength: length,\n                                configureStream: s =\u003e\n                                                 {\n                                                     // 1 MB/sec speed limit for write streams;\n                                                     s.WriteSpeedLimit = 1024 * 1024;\n                                                 });\nstream\n  .Sample(TimeSpan.FromSeconds(0.25)) // Only report progress each 0.25 seconds\n  .Do(p =\u003e Console\n            .WriteLine($\"Downloaded {ByteSize.FromBytes(p.Bytes).ToBinaryString()}\"+\n                       $\"/{ByteSize.FromBytes(p.TotalBytes ?? 0).ToBinaryString()}\"+\n                       $\"({p.Percentage:N2}%) \"+\n                       $\"{ByteSize.FromBytes(p.BytesPerSecond).ToBinaryString()}/sec\"))\n  .Subscribe();\nawait response.Content.CopyToAsync(stream);\n```\n\n- changing speed limits in realtime:\n\n```csharp\nstream.ModifyOptions(x =\u003e x.WriteSpeedLimit = null);\n```\n\n**[`ByteSizeLib`](https://www.nuget.org/packages/ByteSize) library is not a dependency, and only used in this example to provide sample codes that will print progress with better formatted byte sizes**\n\n_example output:_\n\n```powershell\n...\nDownloaded 4.7 MiB/4.69 GiB (0.10%) 680 KiB/sec\nDownloaded 5.03 MiB/4.69 GiB (0.10%) 1016 KiB/sec\nDownloaded 5.04 MiB/4.69 GiB (0.10%) 1 MiB/sec\nDownloaded 5.36 MiB/4.69 GiB (0.11%) 328 KiB/sec\nDownloaded 5.7 MiB/4.69 GiB (0.12%) 680 KiB/sec\n```\n\n## API:\n\nthe `ReactiveStream` class implements both `Stream` \u0026 `IObservable\u003cReactiveStream.StreamProgress\u003e`\n\n```csharp\npublic class ReactiveStream : Stream, IObservable\u003cStreamProgress\u003e\n{\n    //* ... *//\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frainxh11%2Fsakontstack.reactivestream","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frainxh11%2Fsakontstack.reactivestream","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frainxh11%2Fsakontstack.reactivestream/lists"}