Repository: xpnas/yopngs
Branch: master
Commit: a5448bf2ae8d
Files: 35
Total size: 83.6 KB
Directory structure:
gitextract_iuwhz8zx/
├── .dockerignore
├── .github/
│ └── workflows/
│ ├── docker-image-release.yml
│ └── docker-image.yml
├── .gitignore
├── LICENSE
├── README.md
├── docker-compose.yml
├── yopngs/
│ ├── Controllers/
│ │ └── ApiController.cs
│ ├── Dockerfile
│ ├── IStore/
│ │ ├── IDataStore.cs
│ │ ├── IStoreCheck.cs
│ │ ├── IStoreCompress.cs
│ │ ├── SotreCenter.cs
│ │ ├── StoreBase.cs
│ │ ├── StoreCheckNsfwWarperResetAPI.cs
│ │ ├── StoreCompressPngQuantWarper.cs
│ │ └── StoreCompressTinyWarper.cs
│ ├── Program.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── Stores/
│ │ ├── B2Store.cs
│ │ ├── COSStore.cs
│ │ ├── DISKStore.cs
│ │ ├── GithubStore.cs
│ │ ├── OSSStore.cs
│ │ └── S3Store.cs
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ ├── defaultsetting.json
│ ├── wwwroot/
│ │ ├── index.html
│ │ └── static/
│ │ ├── file.js
│ │ └── style.css
│ ├── yopngs.csproj
│ └── yopngs.runtimeconfig.json
└── yopngs.sln
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
================================================
FILE: .github/workflows/docker-image-release.yml
================================================
name: Docker Image release
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker Build & Push to Docker Hub
uses: docker/build-push-action@v2
with:
context: .
file: ./yopngs/Dockerfile
platforms: linux/amd64
push: true
tags: xpnas/yopngs:latest
- name: 'Report Suecss'
run: curl ${{ secrets.INOTIFY }}/yopngs/latestIsOk!
================================================
FILE: .github/workflows/docker-image.yml
================================================
name: Docker Image master
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker Build & Push to Docker Hub
uses: docker/build-push-action@v2
with:
context: .
file: ./yopngs/Dockerfile
platforms: linux/amd64
push: true
tags: xpnas/yopngs:master
- name: 'Report Suecss'
run: curl ${{ secrets.INOTIFY }}/yopngs/masterIsOk!
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
/iimages/iimages/appsettings.json
/iimages/iimages/wwwroot/.vscode
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 xpnas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# yopngs
示例站点:[有图床](https://yopngs.com)
[](https://github.com/xpnas/yopngs/actions/workflows/docker-image.yml)
一个纯粹的开源图床,聚焦图床核心功能,抛去用户验证、上传限制,自带鉴黄功能
支持鉴黄、支持压缩、支持本地存储、COS存储、OSS存储、B2存储
## 使用方法
### 发布版
请先确认已安装DockerCompose
```
wget "https://raw.githubusercontent.com/xpnas/yopngs/master/docker-compose.yml"
```
```
docker-compose up -d
```
### 配置Nginx代理
``` yml
server
{
location / {
proxy_pass http://localhost:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
```
## 配置存储源
所有配置都在config目录下的setting.json文件,可参照defaultsetting.json修改
### 本地存储
DISKStores节点,支持多个,可使用docker启动命令映射Rclone挂载的磁盘
``` json
"DISKStores": [
{
"diskfloder": "/yopngs",//本地目录,docker请做映射
"webfloder": "/v1",//url目录,如https://yopngs.com/v1/2022/01/01/xxxxx.png
"name": "yopngs",//主界面下拉显示名称,随意填写
"type": "yopngs",//内部类型,随意填写
"index": 0,//主界面下拉排序,越小越优先
"active": true//是否激活
},
```
### Backblaze2存储
B2Stores节点,支持多个
```json
"B2Stores": [
{
"KeyId": "xx",
"ApplicationKey": "xx",
"BucketId": "xx",
"Domain": "https://xx.com",//建议在B2前套上Cloudflare,使用自定义域名
"Safe":false,//建议使用Cloudflare规则以避免暴露B2信息,防止有心人刷B2流量,开启后将去除Url中的file/BucketName
"name": "backblazeb2",
"type": "backblazeb2",
"index": "2",
"active": true
}
```
### 腾讯COS存储
COSStores节点,支持多个
``` json
"COSStores": [
{
"region": "ap-shanghai",
"bucket": "xx",
"SECRET_ID": "xx",
"SECRET_KEY": "xx",
"Domain": "https://xx.com",
"name": "COS",
"type": "COS",
"index": 1,
"active": false
}
],
```
### 阿里OSS存储
OSSStores节点,支持多个
``` json
"OSSStores": [
{
"AccessKeyId": "xxx",
"AccessKeySecret": "xx",
"Endpoint": "xx",
"Domain": "https://xx.com",
"name": "OSS",
"type": "OSS",
"index": "2",
"active": false
}
],
```
## 其他设置
```json
"GLOBAL": {
"SIZELIMIT": 30,//图片大小
"EXTLIMIT": ".PNG.GIF.JPG.JPEG.BMP",//类型限制
"NSFW": true,//鉴黄开关
"NSFWCORE": 0.5,//鉴黄分数0~1
"NSFWHOST": "http://nsfwapi:5000",//请勿修改
"SERVERHOST": "http://yopngs:80",//请勿修改
"COMPRESS": false,//是否启用压缩
"COUNT": 0,
"STARTDATE": "2020.01.01"
},
```
================================================
FILE: docker-compose.yml
================================================
version: '2'
services:
yopngs:
container_name: yopngs
restart: always
image: xpnas/yopngs:latest
volumes:
- /yopngs:/yopngs
- /yopngs_config:/app/config
ports:
- "8081:80"
networks:
- yopngs
nsfwapi:
container_name: nsfwapi
restart: always
image: eugencepoi/nsfw_api:latest
environment:
PORT: 5000
links:
- yopngs
ports:
- "8082:5000"
networks:
- yopngs
networks:
yopngs:
driver: bridge
================================================
FILE: yopngs/Controllers/ApiController.cs
================================================
using Iimages.IStore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
namespace Iimages.Controllers
{
[ApiController]
[Route("[controller]")]
public class ApiController : ControllerBase
{
private static object CountValue = "";
private static object DateValue = "";
private IHostEnvironment mEenvironment;
public ApiController(IHostEnvironment hostEnvironment)
{
mEenvironment = hostEnvironment;
CountValue = GetCountValue(false);
}
[HttpPost]
[Route("upload")]
public async Task<IActionResult> UploadAsync(string type)
{
return await Task.Run(() =>
{
//类型检测
IDataStore store;
if (!SotreCenter.Stores.TryGetValue(type, out store))
{
return Ok(new
{
UploadNode = type,
code = 404,
data = new
{
msg = "ERROR",
name = "ERROR",
url = "ERROR",
},
msg = string.Format("上传失败!不支持的类型{0}!", type)
});
}
//文件数量检测
var form = Request.Form;
var files = form.Files;
if (files.Count != 1)
{
return Ok(new
{
UploadNode = type,
code = 404,
data = new
{
msg = "ERROR",
name = "ERROR",
url = "ERROR",
},
msg = "上传失败!"
});
}
//文件大小检测
var formFile = files[0];
var sizeLimit = Convert.ToInt32(SotreCenter.Config["GLOBAL:SIZELIMIT"]);
if (formFile.Length < 0 || formFile.Length > sizeLimit * 1024 * 1024)
{
return Ok(new
{
UploadNode = type,
code = 404,
data = new
{
msg = "ERROR",
name = "ERROR",
url = "ERROR",
},
msg = string.Format("上传失败!限制大小{0}-{1}M", 0, sizeLimit)
});
}
//格式检测
var sourceName = formFile.FileName;
var fileExt = Path.GetExtension(sourceName);
var extLimit = SotreCenter.Config["GLOBAL:EXTLIMIT"];
if (!extLimit.Contains(fileExt.ToUpper()))
{
return Ok(new
{
UploadNode = type,
code = 404,
data = new
{
msg = "ERROR",
name = "ERROR",
url = "ERROR",
},
msg = string.Format("上传失败!限制格式{0}", 0, extLimit)
});
}
try
{
//黄图检测
var localName = Guid.NewGuid().ToString() + fileExt;
var webServer = Request.Scheme + "://" + Request.Host;
var nswFilePath = string.Empty;
using (var memoery = new MemoryStream())
{
memoery.SetLength(formFile.Length);
formFile.CopyTo(memoery);
var maps = memoery.GetBuffer();
//压缩
if (SotreCenter.COMPRESS && (fileExt.ToUpper() == ".PNG" || fileExt.ToUpper() == ".JPG"))
{
maps = SotreCenter.StoreCompress.Compress(maps);
}
//鉴黄
if (SotreCenter.NSFW && !SotreCenter.NSFWCHECK.PassSex(maps))
{
return Ok(new
{
UploadNode = type,
code = 404,
data = new
{
msg = "ERROR",
name = sourceName,
url = "ERROR",
},
msg = "小撸怡情,大撸伤身!"
});
}
//存储
var cdnUrl = string.Empty;
if (store.Up(maps, localName, webServer, ref cdnUrl))
{
CountValue = GetCountValue(true);
return Ok(new
{
UploadNode = type,
code = 200,
data = new
{
msg = "OK",
name = sourceName,
url = cdnUrl,
},
msg = "上传成功!"
});
}
return Ok(new
{
UploadNode = type,
code = 502,
data = new
{
msg = "ERROR",
name = sourceName,
url = "ERROR",
},
msg = "上传失败!"
});
}
}
catch
{
return Ok(new
{
UploadNode = type,
code = 502,
data = new
{
msg = "ERROR",
name = sourceName,
url = "ERROR",
},
msg = "上传失败!"
});
}
});
}
[HttpGet]
[Route("supports")]
public List<SupportType> GetSupports()
{
return SotreCenter.Supports;
}
[HttpGet]
[Route("info")]
public string Info()
{
var startDate = DateTime.ParseExact(GetDateValue() as string, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
var timeSpan = DateTime.Now - startDate;
return string.Format("本站已运行 {0} 天,托管 {1} 张图片", timeSpan.Days, Convert.ToInt64(CountValue));
}
[HttpGet]
public string Get()
{
return "It's Work";
}
private object GetCountValue(bool add)
{
lock (CountValue)
{
var filePath = Path.Combine(mEenvironment.ContentRootPath, "config/count.txt");
if (!System.IO.File.Exists(filePath))
System.IO.File.WriteAllText(filePath, "0");
CountValue = System.IO.File.ReadAllText(filePath);
if (add)
{
CountValue = (Convert.ToInt64(CountValue) + 1).ToString();
System.IO.File.WriteAllText(filePath, CountValue as string);
}
}
return CountValue;
}
private object GetDateValue()
{
lock (DateValue)
{
if (string.IsNullOrEmpty(DateValue as string))
{
var filePath = Path.Combine(mEenvironment.ContentRootPath, "config/date.txt");
if (!System.IO.File.Exists(filePath))
System.IO.File.WriteAllText(filePath, DateTime.Now.ToString("yyyyMMdd"));
DateValue = System.IO.File.ReadAllText(filePath);
}
}
return DateValue;
}
}
}
================================================
FILE: yopngs/Dockerfile
================================================
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["yopngs/yopngs.csproj", "yopngs/"]
RUN dotnet restore "yopngs/yopngs.csproj"
COPY . .
WORKDIR "/src/yopngs"
RUN dotnet build "yopngs.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "yopngs.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "yopngs.dll"]
================================================
FILE: yopngs/IStore/IDataStore.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
namespace Iimages.IStore
{
public class SupportType
{
public string Type { get; set; }
public string Name { get; set; }
public int Index { get; set; }
public SupportType(IDataStore store)
{
Name = store.Name;
Type = store.Type;
Index = store.Index;
}
}
public interface IDataStore
{
int Index { get; set; }
string Name { get; set; }
string Type { get; set; }
bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl);
}
public interface IStoreFactory
{
IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration);
}
}
================================================
FILE: yopngs/IStore/IStoreCheck.cs
================================================
namespace Iimages.IStore
{
public interface IStoreCheck
{
bool PassSex(byte[] formFile);
}
}
================================================
FILE: yopngs/IStore/IStoreCompress.cs
================================================
namespace Iimages.IStore
{
public interface IStoreCompress
{
byte[] Compress(byte[] maps);
}
}
================================================
FILE: yopngs/IStore/SotreCenter.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
namespace Iimages.IStore
{
public static class SotreCenter
{
static SotreCenter()
{
StoreFactories = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(e => e.GetInterfaces().Contains(typeof(IStoreFactory)))
.Select(e => Activator.CreateInstance(e))
.OfType<IStoreFactory>()
.ToList();
}
public static void Initialize(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration configuration, IHttpClientFactory httpClientFactory)
{
var stores = StoreFactories.SelectMany(e => e.GetStores(app, configuration)).ToList();
Config = configuration;
Stores = stores.ToDictionary(e => e.Type, e => e);
Supports = stores.Select(e => new SupportType(e)).OrderBy(e => e.Index).ToList();
NSFW = configuration.GetSection("GLOBAL").GetValue<bool>("NSFW");
COMPRESS = configuration.GetSection("GLOBAL").GetValue<bool>("COMPRESS");
NSFWHOST = configuration.GetSection("GLOBAL").GetValue<string>("NSFWHOST");
if (NSFW)
{
NSFWCHECK = new StoreCheckNsfwWarperResetAPI(app, env, configuration, httpClientFactory);
}
if (COMPRESS)
{
StoreCompress = new StoreCompressTinyWarper(app, env, configuration, httpClientFactory);
}
}
public static IConfiguration Config { get; private set; }
public static bool NSFW { get; private set; }
public static string NSFWHOST { get; private set; }
public static bool COMPRESS { get; private set; }
public static List<IStoreFactory> StoreFactories { get; private set; }
public static Dictionary<string, IDataStore> Stores { get; private set; }
public static List<SupportType> Supports { get; private set; }
public static IStoreCheck NSFWCHECK { get; private set; }
public static IStoreCompress StoreCompress { get; private set; }
}
}
================================================
FILE: yopngs/IStore/StoreBase.cs
================================================
namespace Iimages.IStore
{
public abstract class StoreBase : IDataStore
{
public int Index { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public abstract bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl);
}
}
================================================
FILE: yopngs/IStore/StoreCheckNsfwWarperResetAPI.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.FileProviders;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace Iimages.IStore
{
public class StoreCheckNsfwWarperResetAPI : IStoreCheck
{
private readonly string NSFWFLODER;
private readonly IHttpClientFactory HttpClientFactory;
private readonly string SERVERHOST;
private readonly string NSFWHOST;
private readonly double NSFWCORE;
public StoreCheckNsfwWarperResetAPI(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration config, IHttpClientFactory httpClientFactory)
{
HttpClientFactory = httpClientFactory;
if (config.GetSection("GLOBAL") != null)
{
NSFWCORE = Convert.ToDouble(config["GLOBAL:NSFWCORE"]);
NSFWHOST = Convert.ToString(config["GLOBAL:NSFWHOST"]);
SERVERHOST = Convert.ToString(config["GLOBAL:SERVERHOST"]);
NSFWFLODER = Path.Combine(env.ContentRootPath, "nsfw");
if (!Directory.Exists(NSFWFLODER))
{
Directory.CreateDirectory(NSFWFLODER);
}
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(NSFWFLODER),
RequestPath = "/nsfw"
});
}
}
public bool PassSex(byte[] formFile)
{
//保存到鉴黄目录
var localName = Guid.NewGuid().ToString() + ".png";
var nsfwFile = string.Format("{0}/{1}", NSFWFLODER, localName);
File.WriteAllBytes(nsfwFile, formFile);
try
{
var nsfwFileUrl = string.Format("{0}/{1}/{2}", SERVERHOST, "nsfw", localName);
var url = string.Format("{0}{1}", NSFWHOST, "/?url=" + nsfwFileUrl);
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = HttpClientFactory.CreateClient().SendAsync(request).Result;
var result = response.Content.ReadAsStringAsync().Result;
var jsonDoc = System.Text.Json.JsonDocument.Parse(result);
var jsonScore = jsonDoc.RootElement.GetProperty("score");
if (jsonScore.GetDouble() < NSFWCORE)
return true;
return false;
}
catch
{
return false;
}
finally
{
File.Delete(nsfwFile);
}
}
}
}
================================================
FILE: yopngs/IStore/StoreCompressPngQuantWarper.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Minimage;
namespace Iimages.IStore
{
public class StoreCompressPngQuantWarper : IStoreCompress
{
private static PngQuant g_PngQuant;
static StoreCompressPngQuantWarper()
{
var options = new PngQuantOptions()
{
QualityMinMax = (65, 80),
IEBug = false,
Bit = 256
};
g_PngQuant = new PngQuant(options);
}
public StoreCompressPngQuantWarper(IApplicationBuilder app, IConfiguration config)
{
}
public byte[] Compress(byte[] maps)
{
try
{
return g_PngQuant.Compress(maps).Result;
}
catch
{
return maps;
}
}
}
}
================================================
FILE: yopngs/IStore/StoreCompressTinyWarper.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Minimage;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace Iimages.IStore
{
public class StoreCompressTinyWarper : IStoreCompress
{
private readonly IHttpClientFactory HttpClientFactory;
public StoreCompressTinyWarper(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration config, IHttpClientFactory httpClientFactory)
{
HttpClientFactory = httpClientFactory;
}
public byte[] Compress(byte[] maps)
{
try
{
var randomIP = string.Format("{0}.{1},{2},{3}", new Random().Next(1, 254), new Random().Next(1, 254), new Random().Next(1, 254), new Random().Next(1, 254));
var webRequest = WebRequest.Create(new Uri("https://tinypng.com/backend/opt/shrink"));
webRequest.Method = "Post";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Headers.Add("rejectUnauthorized", "false");
webRequest.Headers.Add("Postman-Token", DateTime.Now.ToString());
webRequest.Headers.Add("Cache-Control", "no-cache");
webRequest.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");
webRequest.Headers.Add("X-Forwarded-For", randomIP);
using (var postStream = webRequest.GetRequestStream())
{
var requestStream = webRequest.GetRequestStream();
requestStream.Write(maps, 0, maps.Length);
}
var response = webRequest.GetResponse();
using (Stream stream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
string resuleJson = reader.ReadToEnd();
var compressUrl = JsonDocument.Parse(resuleJson).RootElement.GetProperty("output").GetProperty("url").GetString();
using (WebClient client = new WebClient())
{
using (Stream compressStream = client.OpenRead(compressUrl))
{
int readCount = 0;
int bufferSize = 1 << 17;
var buffer = new byte[bufferSize];
using (var memory = new MemoryStream())
{
while ((readCount = compressStream.Read(buffer, 0, bufferSize)) > 0)
{
memory.Write(buffer, 0, readCount);
}
return memory.ToArray();
}
}
}
}
}
}
catch(Exception ex)
{
return maps;
}
}
}
}
================================================
FILE: yopngs/Program.cs
================================================
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Iimages
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
}
================================================
FILE: yopngs/Properties/launchSettings.json
================================================
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:64028",
"sslPort": 44396
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api",
"publishAllPorts": true,
"useSSL": true
},
"yopngs": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "index.html",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost5000"
}
}
}
================================================
FILE: yopngs/Startup.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.IO;
using System.Net.Http;
namespace Iimages
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpClientFactory httpClientFactory)
{
app.UseCors(options =>
{
options.AllowAnyHeader();
options.AllowAnyMethod();
options.AllowAnyOrigin();
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseStaticFiles();
app.UseFileServer();
if (!Directory.Exists(Path.Combine(env.ContentRootPath, "config")))
Directory.CreateDirectory(Path.Combine(env.ContentRootPath, "config"));
var filePath = Path.Combine(env.ContentRootPath, "config/setting.json");
if (!File.Exists(filePath))
File.Copy(Path.Combine(env.ContentRootPath, "defaultsetting.json"), filePath);
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config/setting.json", optional: true, reloadOnChange: true)
.Build();
IStore.SotreCenter.Initialize(app, env,config, httpClientFactory);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
================================================
FILE: yopngs/Stores/B2Store.cs
================================================
using B2Net;
using B2Net.Models;
using Iimages.IStore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Iimages.Stores
{
public class B2Store : StoreBase
{
private bool mPersistBucket, mSafe;
private string mKeyId, mApplicationKey, mBucketId, mDomain;
public B2Store(string keyId, string applicationKey, string bucketId, bool prsistBucket, string domain,bool safe)
{
mKeyId = keyId;
mApplicationKey = applicationKey;
mBucketId = bucketId;
mPersistBucket = prsistBucket;
mDomain = domain;
mSafe = safe;
}
public override bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl)
{
try
{
var mOptions = new B2Options()
{
KeyId = mKeyId,
ApplicationKey = mApplicationKey,
BucketId = mBucketId,
PersistBucket = mPersistBucket
};
var mClient = new B2Client(mOptions);
var result = mClient.Authorize().Result;
if (result.Authenticated)
{
var timeTag = DateTime.Now.ToString("yyyy/MM/dd/");
var uploadUrl = mClient.Files.GetUploadUrl(mBucketId).Result;
var fileInfo = mClient.Files.Upload(maps, timeTag + localName, uploadUrl).Result;
var fileId = fileInfo.FileId;
if (mSafe)
{
cdnUrl = string.Format("{0}/{1}", mDomain, timeTag + localName);
}
else
{
cdnUrl = string.Format("{0}/file/{1}/{2}", mDomain, result.Capabilities.BucketName, timeTag + localName);
}
return true;
}
return false;
}
catch
{
return false;
}
}
}
public class B2StoreFactory : IStoreFactory
{
public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)
{
var results = new List<IDataStore>();
var selection = configuration.GetSection("B2Stores");
foreach (IConfigurationSection section in selection.GetChildren())
{
var active = section.GetValue<bool>("active");
if (active)
{
var keyId = section.GetValue<string>("KeyId");
var applicationKey = section.GetValue<string>("ApplicationKey");
var bucketId = section.GetValue<string>("BucketId");
var persistBucket = section.GetValue<bool>("PersistBucket");
var domain = section.GetValue<string>("Domain");
var safe = section.GetValue<bool>("Safe");
var index = section.GetValue<int>("index");
var name = section.GetValue<string>("name");
var type = section.GetValue<string>("type");
var store = new B2Store(keyId, applicationKey, bucketId, persistBucket, domain, safe)
{
Name = name,
Type = type,
Index = index
};
results.Add(store);
}
}
return results.ToArray();
}
}
}
================================================
FILE: yopngs/Stores/COSStore.cs
================================================
using COSXML;
using COSXML.Auth;
using COSXML.Transfer;
using Iimages.IStore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
namespace Iimages.Stores
{
public class COSStore : StoreBase
{
private CosXml mCosXml;
private string mBucket, mDomain;
public COSStore(string bucket, string region, string secretId, string secretKey,string domain)
{
long durationSecond = 600;
var config = new CosXmlConfig.Builder().IsHttps(true).SetRegion(region).SetDebugLog(true).Build();
QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider(secretId, secretKey, durationSecond);
mCosXml = new CosXmlServer(config, cosCredentialProvider);
mBucket = bucket;
mDomain = domain;
}
public override bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl)
{
try
{
var cosPath = DateTime.Now.ToString("/yyyy/MM/dd/") + localName;
var putObjectRequest = new COSXML.Model.Object.PutObjectRequest(mBucket, cosPath, maps);
var uploadTask = new COSXMLUploadTask(new COSXML.Model.Object.PutObjectRequest(mBucket, cosPath, maps));
COSXML.Model.Object.PutObjectResult result = mCosXml.PutObject(putObjectRequest);
if (result.IsSuccessful())
{
cdnUrl = string.Format("{0}{1}", mDomain, cosPath);
return true;
}
}
catch (Exception e)
{
Console.WriteLine("CosException: " + e);
}
return false;
}
}
public class CosStoreFactory : IStoreFactory
{
public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)
{
var results = new List<IDataStore>();
var selection = configuration.GetSection("COSStores");
foreach (IConfigurationSection section in selection.GetChildren())
{
var active = section.GetValue<bool>("active");
if (active)
{
var region = section.GetValue<string>("region");
var bucket = section.GetValue<string>("bucket");
var secretId = section.GetValue<string>("SECRET_ID");
var secretKey = section.GetValue<string>("SECRET_KEY");
var domain = section.GetValue<string>("Domain");
var index = section.GetValue<int>("index");
var name = section.GetValue<string>("name");
var type = section.GetValue<string>("type");
var store = new COSStore(bucket, region, secretId, secretKey, domain)
{
Name = name,
Type = type,
Index = index
};
results.Add(store);
}
}
return results.ToArray();
}
}
}
================================================
FILE: yopngs/Stores/DISKStore.cs
================================================
using Iimages.IStore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.FileProviders;
using System;
using System.Collections.Generic;
using System.IO;
namespace Iimages.Stores
{
public class DISKStore : StoreBase
{
private string m_diskFloder, m_webFloder, m_host;
public DISKStore(string diskFloder, string webFloder, string host)
{
m_diskFloder = diskFloder;
m_webFloder = webFloder;
m_host = host;
}
public override bool Up(byte[] maps, string localName, string webServer, ref string cdnUrl)
{
if (!Directory.Exists(m_diskFloder))
Directory.CreateDirectory(m_diskFloder);
var timeTag = DateTime.Now.ToString("/yyyy/MM/dd/");
var filePath = m_diskFloder + timeTag + localName;
var fileDir = Path.GetDirectoryName(filePath);
if (!Directory.Exists(fileDir))
Directory.CreateDirectory(fileDir);
File.WriteAllBytes(filePath,maps);
cdnUrl = string.Format("{0}{1}{2}{3}", string.IsNullOrEmpty(m_host) ? webServer : m_host, m_webFloder, timeTag, localName);
return true;
}
}
public class DiskStoreFactory : IStoreFactory
{
public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)
{
var results = new List<IDataStore>();
var selection = configuration.GetSection("DISKStores");
foreach (IConfigurationSection section in selection.GetChildren())
{
var diskfloder = section.GetValue<string>("diskfloder");
var webfloder = section.GetValue<string>("webfloder");
var host= section.GetValue<string>("host");
if (!Directory.Exists(diskfloder))
Directory.CreateDirectory(diskfloder);
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(diskfloder),
RequestPath = webfloder
});
var active = section.GetValue<bool>("active");
if (active)
{
var name = section.GetValue<string>("name");
var type = section.GetValue<string>("type");
var index = section.GetValue<int>("index");
var store = new DISKStore(diskfloder, webfloder,host)
{
Name = name,
Type = type,
Index = index
};
results.Add(store);
}
}
return results.ToArray();
}
}
}
================================================
FILE: yopngs/Stores/GithubStore.cs
================================================
using Iimages.IStore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Octokit;
using System.Collections.Generic;
using System.IO;
namespace Iimages.Stores
{
public class GithubStore : StoreBase
{
public override bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl)
{
var client = new GitHubClient(new ProductHeaderValue("my-cool-app"));
var basicAuth = new Credentials("username", "password"); // NOTE: not real credentials
client.Credentials = basicAuth;
using (var archiveContents = File.OpenRead("output.zip"))
{ // TODO: better sample
var assetUpload = new ReleaseAssetUpload()
{
FileName = "my-cool-project-1.0.zip",
ContentType = "application/zip",
RawData = archiveContents
};
var repository = client.Repository.Release.Get("octokit", "octokit.net", 1).Result;
var asset = client.Repository.Release.UploadAsset(repository, assetUpload).Result;
}
return true;
}
}
public class GithubFactory : IStoreFactory
{
public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)
{
var results = new List<IDataStore>();
var selection = configuration.GetSection("OSSStores");
foreach (IConfigurationSection section in selection.GetChildren())
{
//var active = section.GetValue<bool>("active");
//if (active)
//{
// var acessKeyId = section.GetValue<string>("AccessKeyId");
// var acessKeySecret = section.GetValue<string>("AccessKeySecret");
// var endPoint = section.GetValue<string>("Endpoint");
// var bucket = section.GetValue<string>("Bucket");
// var domain = section.GetValue<string>("Domain");
// var index = section.GetValue<int>("index");
// var name = section.GetValue<string>("name");
// var type = section.GetValue<string>("type");
// var diskStore = new OSSStore(acessKeyId, acessKeySecret, endPoint, bucket, domain)
// {
// Name = name,
// Type = type,
// Index = index
// };
// results.Add(diskStore);
//}
}
return results.ToArray();
}
}
}
================================================
FILE: yopngs/Stores/OSSStore.cs
================================================
using Aliyun.OSS;
using Iimages.IStore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
namespace Iimages.Stores
{
public class OSSStore : StoreBase
{
private OssClient mOssClient;
private string mDomain;
public string Bucket { get; private set; }
public OSSStore(string accessKeyId, string accessKeySecret, string endpoint, string bucket, string domian)
{
Bucket = bucket;
mDomain = domian;
mOssClient = new OssClient(endpoint, accessKeyId, accessKeySecret);
}
public override bool Up(byte[] maps,string localName, string localUrl, ref string cdnUrl)
{
using (var stream = new MemoryStream(maps))
{
try
{
var cosPath = DateTime.Now.ToString("yyyy/MM/dd/") + localName;
mOssClient.PutObject(Bucket, cosPath, stream);
cdnUrl = string.Format("{0}/{1}", mDomain, cosPath);
return true;
}
catch
{
return false;
}
}
}
}
public class OSSFactory : IStoreFactory
{
public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)
{
var results = new List<IDataStore>();
var selection = configuration.GetSection("OSSStores");
foreach (IConfigurationSection section in selection.GetChildren())
{
var active = section.GetValue<bool>("active");
if (active)
{
var acessKeyId = section.GetValue<string>("AccessKeyId");
var acessKeySecret = section.GetValue<string>("AccessKeySecret");
var endPoint = section.GetValue<string>("Endpoint");
var bucket = section.GetValue<string>("Bucket");
var domain = section.GetValue<string>("Domain");
var index = section.GetValue<int>("index");
var name = section.GetValue<string>("name");
var type = section.GetValue<string>("type");
var store = new OSSStore(acessKeyId, acessKeySecret, endPoint, bucket,domain)
{
Name = name,
Type = type,
Index = index
};
results.Add(store);
}
}
return results.ToArray();
}
}
}
================================================
FILE: yopngs/Stores/S3Store.cs
================================================
using Amazon.S3;
using Amazon.S3.Model;
using Iimages.IStore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
namespace Iimages.Stores
{
public class S3Store : StoreBase
{
AmazonS3Client Client;
string Domain;
public S3Store(string secretID, string secretKey, string region, string domain)
{
Domain = domain;
var endPoint = new AmazonS3Config();
endPoint.ServiceURL = region;
// endPoint.AuthenticationRegion = region;
Client = new AmazonS3Client(secretID, secretKey, endPoint);
}
public override bool Up(byte[] maps, string localName, string localUrl, ref string cdnUrl)
{
var files= Client.ListBucketsAsync().Result ;
var putRequest2 = new PutObjectRequest
{
BucketName = "xpnas-assets",
//获取和设置键属性。此键用于标识S3中的对象,上传到s3的路径+文件名,
//S3上没有文件夹可以创建一个,参考https://www.cnblogs.com/web424/p/6840207.html
Key = localName,
//所有者获得完全控制权,匿名主体被授予读访问权。如果
//此策略用于对象,它可以从浏览器中读取,无需验证
CannedACL = S3CannedACL.PublicRead,
//上传的文件路径
FilePath = @"C:\Users\Administrator\Desktop\favicon.png",
//为对象设置的标记。标记集必须编码为URL查询参数
TagSet = new List<Tag>{ new Tag { Key = "Test", Value = "S3Test"} }
//ContentType = "image/png"
};
// putRequest2.Metadata.Add("x-amz-meta-title", "AwsS3Net");
PutObjectResponse response2 = Client.PutObjectAsync(putRequest2).Result;
return false;
}
}
public class S3StoreFactory : IStoreFactory
{
public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configuration)
{
var results = new List<IDataStore>();
var selection = configuration.GetSection("S3Stores");
foreach (IConfigurationSection section in selection.GetChildren())
{
var active = section.GetValue<bool>("active");
if (active)
{
var secretID = section.GetValue<string>("secretID");
var secretKey = section.GetValue<string>("secretKey");
var region = section.GetValue<string>("region");
var domain = section.GetValue<string>("Domain");
var index = section.GetValue<int>("index");
var name = section.GetValue<string>("name");
var type = section.GetValue<string>("type");
var store = new S3Store(secretID, secretKey, region, domain)
{
Name = name,
Type = type,
Index = index
};
results.Add(store);
}
}
return results.ToArray();
}
}
}
================================================
FILE: yopngs/appsettings.Development.json
================================================
{
"AllowedHosts": "*",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
================================================
FILE: yopngs/appsettings.json
================================================
{
"AllowedHosts": "*",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
================================================
FILE: yopngs/defaultsetting.json
================================================
{
"GLOBAL": {
"SIZELIMIT": 30,
"EXTLIMIT": ".PNG.GIF.JPG.JPEG.BMP",
"NSFW": true,
"NSFWCORE": 0.5,
"NSFWHOST": "http://nsfwapi:5000",
"SERVERHOST": "http://yopngs:80",
"COMPRESS": false,
"COUNT": 0,
"STARTDATE": "2020.01.01"
},
"DISKStores": [
{
"diskfloder": "/yopngs",
"webfloder": "/v1",
"name": "yopngs",
"type": "yopngs",
"index": 0,
"active": true
}
]
//"B2Stores": [
// {
// "KeyId": "xx",
// "ApplicationKey": "xx",
// "BucketId": "xx",
// "Domain": "xx",
// "Safe"": false
// "name": "backblazeb2",
// "type": "backblazeb2",
// "index": "2",
// "active": true
// }
//"OSSStores": [
// {
// "AccessKeyId": "ap-shanghai",
// "AccessKeySecret": "xx-xx",
// "Endpoint": "xx",
// "Domain": "https://xx.com",
// "name": "OSS",
// "type": "OSS",
// "index": "2",
// "active": false
// }
//],
//"COSStores": [
// {
// "region": "ap-shanghai",
// "bucket": "xx-xx",
// "SECRET_ID": "xx",
// "SECRET_KEY": "xx",
// "Domain": "https://xx.com",
// "name": "COS",
// "type": "COS",
// "index": 1,
// "active": false
// }
//],
}
================================================
FILE: yopngs/wwwroot/index.html
================================================
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, shrink-to-fit=no, viewport-fit=cover">
<title>有图床</title>
<meta name="keywords" content="纯粹图床,免费图床,COS图床,OOS图床" />
<link rel="shortcut icon" href="./favicon.png" />
<link rel="stylesheet" href="./static/style.css">
<script src="./static/jquery.min.js"></script>
<script src="./static/file.js"></script>
</head>
<body>
<input id="file" type="file" multiple="multiple" style="display: none;">
<div class="container">
<div class="upload">
<div class="title">
开始上传
<select id="type" value="ali">
</select>
</div>
<div class="content" id="dragbox">
<svg class="icon" viewBox="0 0 1335 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path
d="M1097.060174 392.125217C1073.730783 172.966957 893.261913 0.378435 666.089739 0.378435c-227.127652 0-415.610435 171.920696-430.948174 391.746782C101.910261 415.454609 0 525.356522 0 666.601739c0 149.147826 125.239652 274.476522 274.476522 274.476522h195.828869v-78.669913H274.476522a193.691826 193.691826 0 0 1-195.940174-195.806609c0-102.021565 70.678261-180.580174 172.588522-195.917913l54.561391-8.013913 8.013913-62.553043c16.005565-180.580174 172.588522-321.157565 352.389565-321.157566 180.580174 0 337.029565 141.356522 352.389565 321.157566v62.553043l62.664348 8.013913c101.910261 16.005565 172.477217 93.896348 172.477218 195.917913 0 109.901913-85.904696 195.806609-195.806609 195.806609h-195.917913v78.580869h196.029217c149.147826 0 274.476522-125.261913 274.476522-274.476521 0-141.133913-101.999304-259.072-235.25287-274.387479"
p-id="2345" fill="#909399"></path>
<path
d="M612.218435 364.766609l1.335652 2.003478L389.698783 590.58087l55.229217 55.362782 181.938087-181.938087V1018.88h78.558609v-78.58087h156.471652-156.471652V458.039652l183.808 183.919305 55.340521-55.340522-277.147826-277.058783-55.229217 55.229218z m-141.913044 575.666087h156.471652-156.716521 0.222608z"
p-id="2346" fill="#909399"></path>
</svg>
<p class="desc">点击上传 / 粘贴上传 / 拖拽上传</p>
</div>
</div>
<div class="filelist">
<div class="title">
上传列表
<div class="copyall" style="display:none">
<button onclick="sel(this);" name="xkx" id="_url">URL</button>
<button onclick="sel(this);" name="xkx" id="_html">HTML</button>
<button onclick="sel(this);" name="xkx" id="_Ubb">UBB</button>
<button onclick="sel(this);" name="xkx" id="_markdown">MD</button>
<button onclick="copyAll(this);" name="xkx" id="copyAll"
style="width:70px;background-color: #d0dbee;">
复制全部
</button>
</div>
</div>
<div class="list"></div>
</div>
</div>
<div id="footer" style="position:fixed;width: 100%;text-align: center;bottom: 0px;display: none;">
<div style="height: 20px;" >
本站已开启鉴黄
</div>
<div style="height: 20px" id="info">
</div>
</div>
</body>
</html>
================================================
FILE: yopngs/wwwroot/static/file.js
================================================
/*
* @xkx
*/
$(() => {
let host = window.location.origin + '/api';
//$(document).ready(function () {
$.ajax({
url: host + '/supports',
type: 'get',
success: (res) => {
let selid = document.getElementById('type');
for (let i = 0; i < res.length; i++) {
selid.options[i] = new Option(res[i].name, res[i].type);
}
},
});
$.ajax({
url: host + '/info',
type: 'get',
success: (res) => {
let info = document.getElementById('info');
info.innerText = res;
},
});
// });
/* 临时粘贴上传 */
$(document).on('paste', (event) => {
let clipboardData =
event.clipboardData ||
window.clipboardData ||
event.originalEvent.clipboardData;
/* 判断是否支持粘贴上传 */
if (!clipboardData || !clipboardData.items)
return alert('当前浏览器不支持粘贴上传');
let items = clipboardData.items;
let file = null;
/* 判断剪切板的内容是否是桌面类型的文件 */
if (items.length === 0) return alert('剪切板内无内容或不支持桌面文件');
/* 开始循环剪切板的内容,判断是否是文件类型,如果是文件类型,则push */
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
file = items[i].getAsFile();
}
}
if (!file) return alert('剪切板内无内容或不支持桌面文件');
upload(new Array(file));
});
/* 点击上传 */
$('.upload .content').on('click', function () {
$('#file').click();
});
/* 监听点击上传 */
$('#file').on('change', () => {
upload($('#file')[0].files);
});
/* 拖拽上传 */
$('#dragbox').on('dragover', (e) => {
e.preventDefault();
});
$('#dragbox').on('dragenter', (e) => {
e.preventDefault();
$('.upload').addClass('dragenter');
});
$('#dragbox').on('dragleave', (e) => {
e.preventDefault();
$('.upload').removeClass('dragenter');
});
$('#dragbox').on('drop', (e) => {
e.preventDefault();
$('.upload').removeClass('dragenter');
let files = e.originalEvent.dataTransfer.files;
upload(files);
});
/* 上传函数 */
function upload(files) {
//if ($('#type').val().trim() === '') return alert('请输入');
for (let i = 0; i < files.length; i++) {
var animateimg = files[i].name;
var imgarr = animateimg.split('\\');
var myimg = imgarr[imgarr.length - 1];
var houzui = myimg.lastIndexOf('.');
var ext = myimg.substring(houzui, myimg.length).toUpperCase();
var file = files[i];
if (!file) {
return false;
}
var fileSize = file.size;
var maxSize = 50 * 1024 * 1024;
if (
ext != '.PNG' &&
ext != '.GIF' &&
ext != '.JPG' &&
ext != '.JPEG' &&
ext != '.BMP'
) {
parent.alert('文件类型错误,请上传图片类型');
$('#file').val(null);
return false;
} else if (parseInt(fileSize) >= parseInt(maxSize)) {
parent.alert('上传的文件不能超过' + maxSize / 1024 / 1024 + 'MB');
return false;
} else {
document.querySelector('.container').classList.add('start');
var type = $('#type').val();
var api = host + '/upload?type=';
let formData = new FormData();
formData.append('image', files[i]);
let randomClass = Date.now().toString(36);
$('.filelist .list').append(`
<div class="item ${randomClass}">
<div class="file">
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M961.536 531.251c-0.614-3.481-1.843-7.168-3.891-10.445L827.392 306.79v-28.876c0-10.445 0-20.276 0.205-29.901 0.819-89.703 1.433-174.285-101.376-179.2H303.923c-33.587 0-58.368 8.601-76.185 26.624-30.72 30.925-30.31 79.257-29.696 140.288 0 8.601 0.204 17.408 0.204 26.419v38.093c-2.867 2.253-5.324 4.915-7.168 8.192L64.717 523.879c-1.639 2.867-2.663 5.734-3.277 8.806-6.144 12.288-9.626 26.01-9.626 40.345v290.407c0 50.585 41.984 91.75 93.594 91.75h733.184c51.61 0 93.594-41.165 93.594-91.75V573.03c-0.205-14.95-4.096-29.286-10.65-41.779zM861.389 481.28h-33.997v-55.91l33.997 55.91zM271.565 138.65c5.53-5.53 16.384-8.397 32.358-8.397h420.045c36.25 1.843 42.803 11.264 41.78 117.145 0 9.83-0.206 19.866-0.206 30.516V481.28H664.576c-16.998 0-30.925 13.722-30.925 30.925 0 64.307-54.681 116.736-122.06 116.736S389.53 576.512 389.53 512.205c0-16.999-13.722-30.925-30.925-30.925H259.89V262.144c0-9.42 0-18.432-0.205-27.034-0.41-43.008-0.819-83.558 11.879-96.46z m-73.523 279.552v63.078h-36.864l36.864-63.078z m712.294 445.44c0 16.179-14.54 30.105-31.949 30.105H145.203c-17.203 0-31.949-13.721-31.949-30.105V573.03c0-16.179 14.541-30.105 31.95-30.105h185.548c15.155 83.763 90.522 147.456 181.043 147.456s165.888-63.898 181.043-147.456h185.55c17.202 0 31.948 13.721 31.948 30.105v290.612z" p-id="14163" fill="#909399"></path>
<path d="M385.638 278.528H655.77c16.998 0 30.924-13.722 30.924-30.925s-13.721-30.925-30.924-30.925H385.638c-16.998 0-30.924 13.722-30.924 30.925s13.926 30.925 30.924 30.925z m-30.924 70.451c0 16.999 13.721 30.925 30.924 30.925H655.77c16.998 0 30.924-13.722 30.924-30.925 0-17.203-13.721-30.925-30.924-30.925H385.638c-16.998 0-30.924 13.927-30.924 30.925z" p-id="14164" fill="#909399"></path>
</svg>
<div class="desc">
<div class="desc__name">${files[i].name}</div>
<div class="desc__size">SIZE:${formatBytes(files[i].size)}</div>
</div>
<a href="javascript: void(0);" class="link">
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M82.603621 941.396379A280.234044 280.234044 0 0 1 0.001138 742.001267a280.063378 280.063378 0 0 1 82.659372-199.110669l145.919675-145.919676a46.535008 46.535008 0 0 1 65.820299 0 46.535008 46.535008 0 0 1 0 65.820298L148.480808 608.710896a187.732916 187.732916 0 0 0-55.352766 133.574814 187.732916 187.732916 0 0 0 55.352766 133.517926 187.732916 187.732916 0 0 0 133.574814 55.352766A187.732916 187.732916 0 0 0 415.289104 875.519192l145.919676-145.919676a46.535008 46.535008 0 0 1 65.820298 0 46.535008 46.535008 0 0 1 0 65.820299l-145.862787 145.919675A280.347821 280.347821 0 0 1 281.998733 1023.998862a280.120266 280.120266 0 0 1-199.395112-82.602483z m292.408239-292.465128a46.42123 46.42123 0 0 1 0-65.820298l208.099093-208.042204a46.478119 46.478119 0 0 1 65.820298 0 46.535008 46.535008 0 0 1 0 65.820298l-208.099093 208.042204a46.42123 46.42123 0 0 1-32.938593 13.653303 46.307453 46.307453 0 0 1-32.824816-13.653303z m354.587656-21.674618a46.535008 46.535008 0 0 1 0-65.820298L875.576081 415.289104a187.732916 187.732916 0 0 0 55.352766-133.517926 187.732916 187.732916 0 0 0-55.352766-133.517925 187.732916 187.732916 0 0 0-133.574814-55.352766 187.334695 187.334695 0 0 0-133.517926 55.352766L462.506777 294.172929a46.591896 46.591896 0 0 1-65.820299 0 46.591896 46.591896 0 0 1 0-65.820299l145.976565-145.919675A279.9496 279.9496 0 0 1 742.001267 0.001138a280.120266 280.120266 0 0 1 199.110668 82.602483A280.290933 280.290933 0 0 1 1023.998862 281.998733a280.120266 280.120266 0 0 1-82.602483 199.110669l-145.919676 145.919676a46.42123 46.42123 0 0 1-32.881704 13.596414 46.535008 46.535008 0 0 1-32.938594-13.368859z" p-id="8129" fill="#909399"></path>
</svg>
</a>
<a title="删除" href="#" class="link" onclick="del(this);return false;">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50.13 50.13" style="enable-background:new 0 0 50.13 50.13;" xml:space="preserve">
<g>
<path style="fill:#010002;" d="M4.574,13.902h40.982c0.345,0,0.625-0.28,0.625-0.625V8.952c0-0.345-0.28-0.625-0.625-0.625H33.315 V0.625C33.315,0.28,33.035,0,32.69,0H17.442c-0.345,0-0.625,0.28-0.625,0.625v7.702H4.574c-0.345,0-0.625,0.28-0.625,0.625v4.325 C3.949,13.622,4.23,13.902,4.574,13.902z M5.199,9.577h12.242c0.345,0,0.625-0.28,0.625-0.625V1.25h13.998v7.702 c0,0.345,0.28,0.625,0.625,0.625h12.242v3.075H5.199V9.577z"/>
<path style="fill:#010002;" d="M11.006,18.06H7.17c-0.345,0-0.625,0.28-0.625,0.625v30.82c0,0.345,0.28,0.625,0.625,0.625h35.791 c0.345,0,0.625-0.28,0.625-0.625v-30.82c0-0.345-0.28-0.625-0.625-0.625h-3.836c-0.345,0-0.625,0.28-0.625,0.625v24.68h-2.537 v-24.68c0-0.345-0.28-0.625-0.625-0.625h-4.324c-0.345,0-0.625,0.28-0.625,0.625v24.68h-2.535v-24.68 c0-0.345-0.28-0.625-0.625-0.625h-4.324c-0.345,0-0.625,0.28-0.625,0.625v24.68h-2.537v-24.68c0-0.345-0.28-0.625-0.625-0.625 h-4.325c-0.345,0-0.625,0.28-0.625,0.625v24.68h-2.537v-24.68C11.631,18.34,11.351,18.06,11.006,18.06z M14.793,44.614 c0.345,0,0.625-0.28,0.625-0.625V19.31h3.074v24.68c0,0.345,0.28,0.625,0.625,0.625h3.787c0.345,0,0.625-0.28,0.625-0.625V19.31 h3.074v24.68c0,0.345,0.28,0.625,0.625,0.625h3.785c0.345,0,0.625-0.28,0.625-0.625V19.31h3.074v24.68 c0,0.345,0.28,0.625,0.625,0.625h3.787c0.345,0,0.625-0.28,0.625-0.625V19.31h2.586v29.57H7.795V19.31h2.586v24.68 c0,0.345,0.28,0.625,0.625,0.625L14.793,44.614L14.793,44.614z"/>
</g>
</svg>
</a>
</div>
<div class="progress">
<div class="progress-bar">
<div class="progress-inner"></div>
</div>
<div class="progress-status">
<svg class="icon status-success" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M310.710857 768.292571z m402.578286 0z" p-id="11696" fill="#67C23A"></path>
<path d="M512 0C229.376 0 0 229.376 0 512S229.376 1024 512 1024 1024 794.624 1024 512 794.624 0 512 0z m300.178286 373.321143l-354.011429 354.011428c-21.065143 21.065143-55.588571 21.065143-76.653714 0l-169.691429-169.106285c-21.065143-21.065143-21.065143-55.588571 0-76.653715 21.065143-21.065143 55.588571-21.065143 76.653715 0l131.072 131.072 315.392-315.392c21.065143-21.065143 55.588571-21.065143 76.653714 0 21.650286 20.48 21.650286 55.003429 0.585143 76.068572z" p-id="11697" fill="#67C23A"></path>
</svg>
<svg class="icon status-error" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M733.678921 290.121702c22.43209 22.531789 22.43209 58.921624 0 81.353714L593.403583 511.850453 733.678921 652.125791c20.537825 20.537825 22.531789 53.13913 4.785513 76.069711l-4.785513 5.4834c-22.531789 22.531789-58.921624 22.531789-81.453412 0L511.95017 593.204167 371.674832 733.579204c-20.537825 20.537825-53.13913 22.531789-76.069711 4.785512l-5.383702-4.785512c-22.531789-22.531789-22.531789-58.921624 0-81.453413l140.275339-140.275338L290.321118 371.674813c-20.637523-20.537825-22.731185-53.13913-4.885211-76.169409l4.785512-5.383702c22.531789-22.43209 58.921624-22.43209 81.353715 0l140.275338 140.17564L652.225509 290.121702c20.537825-20.537825 53.039431-22.531789 75.970012-4.785513l5.4834 4.785513zM0.000019 511.850453c0 282.744037 229.206114 511.950151 511.950151 511.950151s511.950151-229.206114 511.950151-511.950151S794.694207 0 511.95017 0 0.000019 229.206114 0.000019 511.850453z" p-id="14360" fill="#F56C6C"></path>
</svg>
</div>
</div>
<div class="result" style="display:none">
<input value="" id="Imgs_url" >URL</input>
<input value="" id="Imgs_html">HTML</input>
<input value="" id="Imgs_Ubb">BBCode</input>
<input value="" id="Imgs_markdown">Markdown</input>
</div>
<input id="show" name="show" onclick="oCopy(this)" type="text" value="" readonly style="display:none">
</div>
`);
$.ajax({
url: api + $('#type').val(),
type: 'post',
dataType: 'json',
processData: false,
contentType: false,
data: formData,
xhr: () => {
let xhr = $.ajaxSettings.xhr();
if (!xhr.upload) return;
xhr.upload.addEventListener(
'progress',
(e) => {
let percent = Math.floor((e.loaded / e.total) * 100);
$('.' + randomClass)
.find('.progress-inner')
.css('width', percent + '%');
},
false
);
return xhr;
},
success: (res) => {
if (type == 'muke') {
var imgSrc = res.data.url.muke;
} else if (type == 'vxichina') {
var imgSrc = res.data.url.vxichina;
} else if (type == 'qihoo') {
var imgSrc = res.data.url.qihoo;
} else if (type == 'catbox') {
var imgSrc = res.data.url.catbox;
} else if (type == 'gtimg') {
var imgSrc = res.data.url.gtimg;
} else {
var imgSrc = res.data.url;
}
/* 清除input框 */
$('#file').val(null);
if (res.code === -1) {
$('.' + randomClass).fadeOut();
alert(res.data.url);
} else {
if (res.code === 200) {
$('.' + randomClass)
.find('.progress-inner')
.addClass('success');
$('.' + randomClass)
.find('.status-success')
.show();
$('.' + randomClass)
.find('.link')
.attr({
href: imgSrc,
target: '_blank',
});
//代码链接xkx
$('.' + randomClass)
.find('#Imgs_url')
.attr({
value: imgSrc,
});
$('.' + randomClass)
.find('#Imgs_html')
.attr({
value: '<img src="' + imgSrc + '"/>',
});
$('.' + randomClass)
.find('#Imgs_Ubb')
.attr({
value: '[img]' + imgSrc + '[/img]',
});
$('.' + randomClass)
.find('#Imgs_markdown')
.attr({
value: '',
});
//显示链接xkx
$('.' + randomClass)
.find('#show')
.show();
$('.' + randomClass)
.find('#show')
.attr({
value: imgSrc,
});
//复制所有xkx
$('.copyall').show();
var tt = $('.filelist .title').html().replace('上传列表', '');
$('.filelist .title').html(tt);
} else {
$('.' + randomClass)
.find('.progress-inner')
.addClass('error');
$('.' + randomClass)
.find('.status-error')
.show();
$('.' + randomClass)
.find('#show')
.show();
$('.' + randomClass)
.find('#show')
.attr({
value: res.msg,
});
}
}
},
fail: () => {
$('.' + randomClass).fadeOut();
},
});
}
}
}
/* 获取文件大小 */
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + sizes[i];
}
});
function del(obj) {
var item = obj.parentNode.parentNode;
item.parentNode.removeChild(item);
}
function sel(obj) {
for (var i = 0; i < document.querySelectorAll('#Imgs' + obj.id).length; i++) {
document.querySelectorAll('#Imgs' + obj.id)[
i
].parentElement.nextElementSibling.value = document.querySelectorAll(
'#Imgs' + obj.id
)[i].value;
}
}
function copyAll(obj) {
var xkx = '';
for (var i = 0; i < document.querySelectorAll('#show').length; i++) {
var xkx = xkx + document.querySelectorAll('#show')[i].value + '\n';
}
var txa = document.createElement('textarea');
txa.value = xkx;
document.body.appendChild(txa);
txa.select();
var res = document.execCommand('copy');
document.body.removeChild(txa);
console.log('copy success');
console.log(xkx);
if (browserRedirect()) {
alert('设备类型为手机,有一定几率复制失败!请查看剪切板是否成功复制');
}
}
function oCopy(obj) {
obj.select();
document.execCommand('Copy');
console.log(obj.value);
if (browserRedirect()) {
alert('设备类型为手机,有一定几率复制失败!请查看剪切板是否成功复制');
}
}
function browserRedirect() {
var sUserAgent = navigator.userAgent.toLowerCase();
var bIsIpad = sUserAgent.match(/ipad/i) == 'ipad';
var bIsIphone = sUserAgent.match(/iphone os/i) == 'iphone os';
var bIsMidp = sUserAgent.match(/midp/i) == 'midp';
var bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == 'rv:1.2.3.4';
var bIsUc = sUserAgent.match(/ucweb/i) == 'web';
var bIsCE = sUserAgent.match(/windows ce/i) == 'windows ce';
var bIsWM = sUserAgent.match(/windows mobile/i) == 'windows mobile';
var bIsAndroid = sUserAgent.match(/android/i) == 'android';
if (
bIsIpad ||
bIsIphone ||
bIsMidp ||
bIsUc7 ||
bIsUc ||
bIsCE ||
bIsWM ||
bIsAndroid
) {
return 1;
}
}
================================================
FILE: yopngs/wwwroot/static/style.css
================================================
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 3.5px;
background-color: rgba(50, 50, 50, 0.3);
}
::-webkit-scrollbar-track {
border-radius: 3.5px;
background-color: rgba(50, 50, 50, 0.1);
}
html {
height: 100%;
}
@-webkit-keyframes Gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
@keyframes Gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
body {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(
-45deg,
rgba(9, 69, 138, 0.2),
rgba(68, 155, 255, 0.7),
rgba(117, 113, 251, 0.7),
rgba(68, 155, 255, 0.7),
rgba(9, 69, 138, 0.2)
);
background-size: 400% 400%;
-webkit-animation: Gradient 15s ease infinite;
animation: Gradient 15s ease infinite;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
font-size: 14px;
}
.container {
display: grid;
gap: 20px;
grid-template-columns: 500px 250px;
grid-template-rows: 500px;
}
.container.start {
display: grid;
gap: 20px;
grid-template-columns: 250px 500px;
grid-template-rows: 500px;
}
@-webkit-keyframes slideRight {
0% {
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
}
@keyframes slideRight {
0% {
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
}
@-webkit-keyframes slideLeft {
0% {
-webkit-transform: translateX(50%);
transform: translateX(50%);
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
}
@keyframes slideLeft {
0% {
-webkit-transform: translateX(50%);
transform: translateX(50%);
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
}
.upload,
.filelist {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.upload .title,
.filelist .title {
height: 45px;
line-height: 45px;
border-bottom: 1px solid #f2f6fc;
color: #606266;
font-weight: 500;
padding: 0 15px;
font-size: 16px;
}
.upload {
-webkit-animation: slideRight 1.5s;
animation: slideRight 1.5s;
transition: box-shadow 0.35s;
}
.upload .title {
display: flex;
justify-content: space-between;
align-items: center;
}
.copyall button {
width: 70px;
height: 28px;
border-radius: 14px;
margin: 0 12px;
background-color: #fff;
}
.upload .title select {
height: 28px;
padding: 0 5px;
border-radius: 14px;
}
.copyall {
display: inline-flex;
margin: 8px;
}
#show {
width: 475px;
height: 28px;
outline: none;
border: 1px solid #dcdfe6;
padding: 0 15px;
color: #606266;
border-radius: 14px;
font-size: 12px;
transition: border 0.35s;
-webkit-appearance: none;
}
.upload .title input:focus {
border-color: #409eff;
}
.upload .content {
cursor: pointer;
height: calc(100% - 45px);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.upload .content .icon {
width: 80px;
height: 80px;
margin-bottom: 20px;
}
.upload .content .desc {
color: #606266;
}
.upload.dragenter {
box-shadow: 20px 20px 20px 0 rgba(0, 0, 0, 0.25);
}
.upload.dragenter .content > * {
pointer-events: none;
}
.filelist {
-webkit-animation: slideLeft 1.5s;
animation: slideLeft 1.5s;
}
.filelist .list {
height: calc(100% - 45px);
overflow-y: auto;
padding: 10px;
scroll-behavior: smooth;
}
.filelist .list .item {
margin-bottom: 10px;
}
.filelist .list .item:last-child {
margin-bottom: 0;
}
.filelist .list .item .file {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.filelist .list .item .file .icon {
width: 45px;
height: 45px;
margin-right: 10px;
}
.filelist .list .item .file .desc {
flex: 1;
min-width: 0;
}
.filelist .list .item .file .desc__name {
font-size: 15px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-weight: 600;
color: #606266;
margin-bottom: 5px;
}
.filelist .list .item .file .desc__size {
font-size: 12px;
color: #909399;
}
.filelist .list .item .file .link {
display: flex;
align-items: center;
justify-content: center;
padding: 5px;
cursor: pointer;
margin-left: 10px;
border-radius: 50%;
transition: background 0.2s;
}
.filelist .list .item .file .link svg {
width: 18px;
height: 18px;
}
.filelist .list .item .file .link:hover {
background: #f56c6c50;
}
.filelist .list .item .progress {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
}
.filelist .list .item .progress .progress-bar {
flex: 1;
min-width: 0;
}
.filelist .list .item .progress .progress-bar .progress-inner {
width: 0%;
height: 2px;
background: #409eff;
border-radius: 1px;
transition: width 0.2s;
}
.filelist .list .item .progress .progress-bar .progress-inner.success {
background: #67c23a;
}
.filelist .list .item .progress .progress-bar .progress-inner.error {
background: #f56c6c;
}
.filelist .list .item .progress .progress-status {
margin-left: 5px;
}
.filelist .list .item .progress .progress-status .icon {
display: none;
width: 14px;
height: 14px;
}
@media (max-width: 780px) {
body {
height: auto;
padding: 5vh 0;
}
.container {
grid-template-columns: 350px;
grid-template-rows: 400px 150px;
}
.container.start {
grid-template-columns: 350px !important;
grid-template-rows: 190px 400px !important;
}
.copyall button {
width: 50px;
margin: 0 4px;
}
#show {
width: 320px;
padding: 0 11px;
}
}
================================================
FILE: yopngs/yopngs.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>508626fd-dc38-4a3f-9757-1caed8893c4f</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<AssemblyName>yopngs</AssemblyName>
<RootNamespace>yopngs</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
<PackageReference Include="AWSSDK.S3" Version="3.7.7.5" />
<PackageReference Include="B2Net" Version="0.7.5" />
<PackageReference Include="Kirinnee.Minimage" Version="1.1.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.11.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Octokit" Version="0.50.0" />
<PackageReference Include="Tencent.QCloud.Cos.Sdk" Version="5.4.23" />
</ItemGroup>
<ItemGroup>
<None Include="wwwroot\static\file.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="wwwroot\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="wwwroot\static\style.css">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Update="defaultsetting.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Update="pngQuant.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="pngQuant.sh">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties appsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
</Project>
================================================
FILE: yopngs/yopngs.runtimeconfig.json
================================================
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"framework": {
"name": "Microsoft.AspNetCore.App",
"version": "3.1.0"
},
"configProperties": {
"System.GC.Server": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false,
"System.Net.Http.UseSocketsHttpHandler": false
}
}
}
================================================
FILE: yopngs.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31129.286
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "yopngs", "yopngs\yopngs.csproj", "{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79A1F728-0A2D-497A-9C4B-E7B741FCCF3C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {69890056-9C2F-480E-B9EE-2E12395A240B}
EndGlobalSection
EndGlobal
gitextract_iuwhz8zx/ ├── .dockerignore ├── .github/ │ └── workflows/ │ ├── docker-image-release.yml │ └── docker-image.yml ├── .gitignore ├── LICENSE ├── README.md ├── docker-compose.yml ├── yopngs/ │ ├── Controllers/ │ │ └── ApiController.cs │ ├── Dockerfile │ ├── IStore/ │ │ ├── IDataStore.cs │ │ ├── IStoreCheck.cs │ │ ├── IStoreCompress.cs │ │ ├── SotreCenter.cs │ │ ├── StoreBase.cs │ │ ├── StoreCheckNsfwWarperResetAPI.cs │ │ ├── StoreCompressPngQuantWarper.cs │ │ └── StoreCompressTinyWarper.cs │ ├── Program.cs │ ├── Properties/ │ │ └── launchSettings.json │ ├── Startup.cs │ ├── Stores/ │ │ ├── B2Store.cs │ │ ├── COSStore.cs │ │ ├── DISKStore.cs │ │ ├── GithubStore.cs │ │ ├── OSSStore.cs │ │ └── S3Store.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── defaultsetting.json │ ├── wwwroot/ │ │ ├── index.html │ │ └── static/ │ │ ├── file.js │ │ └── style.css │ ├── yopngs.csproj │ └── yopngs.runtimeconfig.json └── yopngs.sln
SYMBOL INDEX (76 symbols across 18 files)
FILE: yopngs/Controllers/ApiController.cs
class ApiController (line 13) | [ApiController]
method ApiController (line 24) | public ApiController(IHostEnvironment hostEnvironment)
method UploadAsync (line 30) | [HttpPost]
method GetSupports (line 205) | [HttpGet]
method Info (line 212) | [HttpGet]
method Get (line 223) | [HttpGet]
method GetCountValue (line 229) | private object GetCountValue(bool add)
method GetDateValue (line 249) | private object GetDateValue()
FILE: yopngs/IStore/IDataStore.cs
class SupportType (line 6) | public class SupportType
method SupportType (line 15) | public SupportType(IDataStore store)
type IDataStore (line 25) | public interface IDataStore
method Up (line 33) | bool Up(byte[] maps, string localName, string localUrl, ref string cdn...
type IStoreFactory (line 37) | public interface IStoreFactory
method GetStores (line 39) | IDataStore[] GetStores(IApplicationBuilder app, IConfiguration configu...
FILE: yopngs/IStore/IStoreCheck.cs
type IStoreCheck (line 3) | public interface IStoreCheck
method PassSex (line 6) | bool PassSex(byte[] formFile);
FILE: yopngs/IStore/IStoreCompress.cs
type IStoreCompress (line 3) | public interface IStoreCompress
method Compress (line 5) | byte[] Compress(byte[] maps);
FILE: yopngs/IStore/SotreCenter.cs
class SotreCenter (line 12) | public static class SotreCenter
method SotreCenter (line 15) | static SotreCenter()
method Initialize (line 25) | public static void Initialize(IApplicationBuilder app, IWebHostEnviron...
FILE: yopngs/IStore/StoreBase.cs
class StoreBase (line 3) | public abstract class StoreBase : IDataStore
method Up (line 11) | public abstract bool Up(byte[] maps, string localName, string localUrl...
FILE: yopngs/IStore/StoreCheckNsfwWarperResetAPI.cs
class StoreCheckNsfwWarperResetAPI (line 17) | public class StoreCheckNsfwWarperResetAPI : IStoreCheck
method StoreCheckNsfwWarperResetAPI (line 25) | public StoreCheckNsfwWarperResetAPI(IApplicationBuilder app, IWebHostE...
method PassSex (line 48) | public bool PassSex(byte[] formFile)
FILE: yopngs/IStore/StoreCompressPngQuantWarper.cs
class StoreCompressPngQuantWarper (line 7) | public class StoreCompressPngQuantWarper : IStoreCompress
method StoreCompressPngQuantWarper (line 13) | static StoreCompressPngQuantWarper()
method StoreCompressPngQuantWarper (line 26) | public StoreCompressPngQuantWarper(IApplicationBuilder app, IConfigura...
method Compress (line 31) | public byte[] Compress(byte[] maps)
FILE: yopngs/IStore/StoreCompressTinyWarper.cs
class StoreCompressTinyWarper (line 17) | public class StoreCompressTinyWarper : IStoreCompress
method StoreCompressTinyWarper (line 24) | public StoreCompressTinyWarper(IApplicationBuilder app, IWebHostEnviro...
method Compress (line 31) | public byte[] Compress(byte[] maps)
FILE: yopngs/Program.cs
class Program (line 6) | public class Program
method Main (line 8) | public static void Main(string[] args)
method CreateHostBuilder (line 13) | public static IHostBuilder CreateHostBuilder(string[] args)
FILE: yopngs/Startup.cs
class Startup (line 11) | public class Startup
method Startup (line 13) | public Startup(IConfiguration configuration)
method ConfigureServices (line 21) | public void ConfigureServices(IServiceCollection services)
method Configure (line 28) | public void Configure(IApplicationBuilder app, IWebHostEnvironment env...
FILE: yopngs/Stores/B2Store.cs
class B2Store (line 12) | public class B2Store : StoreBase
method B2Store (line 20) | public B2Store(string keyId, string applicationKey, string bucketId, b...
method Up (line 30) | public override bool Up(byte[] maps, string localName, string localUrl...
class B2StoreFactory (line 71) | public class B2StoreFactory : IStoreFactory
method GetStores (line 73) | public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration ...
FILE: yopngs/Stores/COSStore.cs
class COSStore (line 12) | public class COSStore : StoreBase
method COSStore (line 19) | public COSStore(string bucket, string region, string secretId, string ...
method Up (line 29) | public override bool Up(byte[] maps, string localName, string localUrl...
class CosStoreFactory (line 54) | public class CosStoreFactory : IStoreFactory
method GetStores (line 56) | public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration ...
FILE: yopngs/Stores/DISKStore.cs
class DISKStore (line 12) | public class DISKStore : StoreBase
method DISKStore (line 17) | public DISKStore(string diskFloder, string webFloder, string host)
method Up (line 25) | public override bool Up(byte[] maps, string localName, string webServe...
class DiskStoreFactory (line 47) | public class DiskStoreFactory : IStoreFactory
method GetStores (line 49) | public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration ...
FILE: yopngs/Stores/GithubStore.cs
class GithubStore (line 10) | public class GithubStore : StoreBase
method Up (line 12) | public override bool Up(byte[] maps, string localName, string localUrl...
class GithubFactory (line 33) | public class GithubFactory : IStoreFactory
method GetStores (line 35) | public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration ...
FILE: yopngs/Stores/OSSStore.cs
class OSSStore (line 11) | public class OSSStore : StoreBase
method OSSStore (line 20) | public OSSStore(string accessKeyId, string accessKeySecret, string end...
method Up (line 27) | public override bool Up(byte[] maps,string localName, string localUrl,...
class OSSFactory (line 47) | public class OSSFactory : IStoreFactory
method GetStores (line 49) | public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration ...
FILE: yopngs/Stores/S3Store.cs
class S3Store (line 10) | public class S3Store : StoreBase
method S3Store (line 16) | public S3Store(string secretID, string secretKey, string region, strin...
method Up (line 28) | public override bool Up(byte[] maps, string localName, string localUr...
class S3StoreFactory (line 53) | public class S3StoreFactory : IStoreFactory
method GetStores (line 55) | public IDataStore[] GetStores(IApplicationBuilder app, IConfiguration ...
FILE: yopngs/wwwroot/static/file.js
function upload (line 78) | function upload(files) {
function formatBytes (line 277) | function formatBytes(bytes, decimals = 2) {
function del (line 286) | function del(obj) {
function sel (line 290) | function sel(obj) {
function copyAll (line 299) | function copyAll(obj) {
function oCopy (line 316) | function oCopy(obj) {
function browserRedirect (line 324) | function browserRedirect() {
Condensed preview — 35 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (94K chars).
[
{
"path": ".dockerignore",
"chars": 316,
"preview": "**/.classpath\n**/.dockerignore\n**/.env\n**/.git\n**/.gitignore\n**/.project\n**/.settings\n**/.toolstarget\n**/.vs\n**/.vscode\n"
},
{
"path": ".github/workflows/docker-image-release.yml",
"chars": 714,
"preview": "name: Docker Image release\n\non:\n release:\n types: [published]\n\njobs:\n\n build:\n\n runs-on: ubuntu-latest\n \n "
},
{
"path": ".github/workflows/docker-image.yml",
"chars": 753,
"preview": "name: Docker Image master\n\non:\n push:\n branches: [ master ]\n pull_request:\n branches: [ master ]\n\njobs:\n\n build"
},
{
"path": ".gitignore",
"chars": 6069,
"preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
},
{
"path": "LICENSE",
"chars": 1062,
"preview": "MIT License\n\nCopyright (c) 2021 xpnas\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
},
{
"path": "README.md",
"chars": 2471,
"preview": "# yopngs\n\n示例站点:[有图床](https://yopngs.com)\n\n[;\n\n }\n}\n"
},
{
"path": "yopngs/IStore/IStoreCompress.cs",
"chars": 116,
"preview": "namespace Iimages.IStore\n{\n public interface IStoreCompress\n {\n byte[] Compress(byte[] maps);\n }\n}\n"
},
{
"path": "yopngs/IStore/SotreCenter.cs",
"chars": 2307,
"preview": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing"
},
{
"path": "yopngs/IStore/StoreBase.cs",
"chars": 316,
"preview": "namespace Iimages.IStore\n{\n public abstract class StoreBase : IDataStore\n {\n public int Index { get; set; "
},
{
"path": "yopngs/IStore/StoreCheckNsfwWarperResetAPI.cs",
"chars": 2863,
"preview": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.Http;\nusing Microsof"
},
{
"path": "yopngs/IStore/StoreCompressPngQuantWarper.cs",
"chars": 903,
"preview": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Minimage;\n\nnamespace Iimages.IStore"
},
{
"path": "yopngs/IStore/StoreCompressTinyWarper.cs",
"chars": 3441,
"preview": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing"
},
{
"path": "yopngs/Program.cs",
"chars": 524,
"preview": "using Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Hosting;\n\nnamespace Iimages\n{\n public class Program\n "
},
{
"path": "yopngs/Properties/launchSettings.json",
"chars": 964,
"preview": "{\n \"iisSettings\": {\n \"windowsAuthentication\": false,\n \"anonymousAuthentication\": true,\n \"iisExpress\": {\n "
},
{
"path": "yopngs/Startup.cs",
"chars": 2244,
"preview": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing "
},
{
"path": "yopngs/Stores/B2Store.cs",
"chars": 3699,
"preview": "using B2Net;\nusing B2Net.Models;\nusing Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.C"
},
{
"path": "yopngs/Stores/COSStore.cs",
"chars": 3236,
"preview": "using COSXML;\nusing COSXML.Auth;\nusing COSXML.Transfer;\nusing Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing"
},
{
"path": "yopngs/Stores/DISKStore.cs",
"chars": 2835,
"preview": "using Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Ext"
},
{
"path": "yopngs/Stores/GithubStore.cs",
"chars": 2671,
"preview": "using Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Octokit;\nusin"
},
{
"path": "yopngs/Stores/OSSStore.cs",
"chars": 2700,
"preview": "using Aliyun.OSS;\nusing Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nu"
},
{
"path": "yopngs/Stores/S3Store.cs",
"chars": 3063,
"preview": "using Amazon.S3;\nusing Amazon.S3.Model;\nusing Iimages.IStore;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Exten"
},
{
"path": "yopngs/appsettings.Development.json",
"chars": 181,
"preview": "{\n \"AllowedHosts\": \"*\",\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Information\",\n \"Microsoft\": \"Warning\",\n"
},
{
"path": "yopngs/appsettings.json",
"chars": 181,
"preview": "{\n \"AllowedHosts\": \"*\",\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Information\",\n \"Microsoft\": \"Warning\",\n"
},
{
"path": "yopngs/defaultsetting.json",
"chars": 1270,
"preview": "{\n \"GLOBAL\": {\n \"SIZELIMIT\": 30,\n \"EXTLIMIT\": \".PNG.GIF.JPG.JPEG.BMP\",\n \"NSFW\": true,\n \"NSFWCORE\": 0.5,\n "
},
{
"path": "yopngs/wwwroot/index.html",
"chars": 3018,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-cn\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\"content=\"width=device-width, us"
},
{
"path": "yopngs/wwwroot/static/file.js",
"chars": 17129,
"preview": "/*\n * @xkx\n */\n$(() => {\n let host = window.location.origin + '/api';\n\n //$(document).ready(function () {\n $.ajax({\n "
},
{
"path": "yopngs/wwwroot/static/style.css",
"chars": 6163,
"preview": "* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n -webkit-tap-highlight-color: transparent;\n}\n::-webkit-scrollb"
},
{
"path": "yopngs/yopngs.csproj",
"chars": 1860,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n <PropertyGroup>\n <TargetFramework>netcoreapp3.1</TargetFramework>\n <User"
},
{
"path": "yopngs/yopngs.runtimeconfig.json",
"chars": 353,
"preview": "{\n \"runtimeOptions\": {\n \"tfm\": \"netcoreapp3.1\",\n \"framework\": {\n \"name\": \"Microsoft.AspNetCore.App\",\n \""
},
{
"path": "yopngs.sln",
"chars": 1097,
"preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.3112"
}
]
About this extraction
This page contains the full source code of the xpnas/yopngs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 35 files (83.6 KB), approximately 24.2k tokens, and a symbol index with 76 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.