StarBlog Development Notes (20): Image Display Optimization
Preface My server bandwidth is relatively high, so when visiting the blog deployed on it I barely notice any loading delay, and I didn't optimize images. But recently some friends said the blog's images load rather slowly, so let's improve the image optimization~ Currently two things need to be improved Image waterfall layout Image thumbnails Image waterfall layout I introduced the waterfall layout in a previous article: Developing a Blog Project with .NetCore - StarBlog - (10) Image Waterfall Layout But at the time I directly used the Bootstrap5 sample code, which had occasional display misalignment
目录7 个章节
Preface
My server bandwidth is relatively high, so when visiting the blog deployed on it I barely notice any loading delay, and I didn't optimize images. But recently some friends said the blog's images load rather slowly, so let's improve the image optimization~
Currently two things need to be improved
- Image waterfall layout
- Image thumbnails
Image Waterfall Layout
I introduced the waterfall layout in a previous article: Developing a Blog Project with .NetCore - StarBlog - (10) Image Waterfall Layout
But at the time I directly used the Bootstrap5 sample code, which had a bug where items would occasionally display misaligned.
I checked the masonry-layout official site again and found the cause of this bug: the waterfall layout was rendered before the images had finished loading, and since the images vary in size, this caused the final display to be misaligned.
The fix is also simple: use the imagesLoaded library, which has an event that fires after all images have loaded; re-render the waterfall layout in the event handler, and you're done~
The masonry-layout library doesn't bundle imagesLoaded; you have to install it yourself to use it.
The version I used is 5.0.0
yarn add imagesloaded
Configure the path in gulpfile.js of StarBlog.Web
// 使用 npm 下载的前端组件,自定义存放位置
const customLibs = [
// ...
{name: 'imagesloaded', dist: './node_modules/imagesloaded/*.js'},
]
Then run gulp move
Next, modify the photo page StarBlog.Web/Views/Photography/Index.cshtml
Import the JS dependencies. I also used the environment tag helper here, which can include different files based on the environment. In the development environment include the full JS files; in the production environment include the *.min files.
@section bottom {
<environment include="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/masonry-layout/dist/masonry.pkgd.js"></script>
<script src="~/lib/imagesloaded/imagesloaded.pkgd.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/masonry-layout/dist/masonry.pkgd.min.js"></script>
<script src="~/lib/imagesloaded/imagesloaded.pkgd.min.js"></script>
</environment>
<script src="~/js/photo.js"></script>
}
Change the image list part of the code
<div id="photo-grid" class="row">
@foreach (var photo in Model.Photos) {
<div class="col-sm-6 col-lg-4 mb-4 grid-item">
<partial name="Widgets/PhotoCard" model="photo"/>
</div>
}
</div>
Then the JS code in js/photo.js
// init Masonry
let $grid = $('#photo-grid').masonry({
itemSelector: '.grid-item',
percentPosition: true,
})
// layout Masonry after each image loads
$grid.imagesLoaded().progress( function() {
$grid.masonry('layout');
});
Done~
Image Thumbnails
At first I used the SixLabors.ImageSharp.Web library
Pretty convenient, used in the form of middleware
After installing via Nuget, configure it in program.cs
// 注册服务
builder.Services.AddImageSharp();
// 添加中间件
app.UseImageSharp();
After that, you can append parameters like width=xxx&format=xxx to any image file URL to resize and convert the format~
I made each image in the photo list into a partial view component, in the file StarBlog.Web/Views/Shared/Widgets/PhotoCard.cshtml
To turn this into a thumbnail, you just need to modify the img element
Now each image thumbnail has a width of 300 (adaptive width), converted to webp format
<img class="bd-placeholder-img card-img-top" src="/media/@Model.FilePath?width=300&format=webp" alt="">
This middleware has many other parameters you can set; I only used two. See the docs for details: https://docs.sixlabors.com/articles/imagesharp.web/processingcommands.html
Progressive JPEG
Using the ImageSharp.Web middleware was already good enough
But there was one shortcoming: it doesn't support generating progressive JPEG images, so you can't show a blurry outline first and then gradually sharpen when loading an image; instead it loads line by line from top to bottom, which doesn't look great~
So I used the Magick.NET library to generate progressive JPEG images.
This library has three versions: Q8, Q16, Q16-HDRI
According to the official docs, I chose the recommended Q8 version, which has the best performance (image quality doesn't matter anymore)
Install the Magick.NET-Q8-AnyCPU library via nuget
Modify StarBlog.Web/Services/PhotoService.cs
Now I'm going to write a method to generate thumbnails
/// <summary>
/// 生成Progressive JPEG缩略图 (使用 MagickImage)
/// </summary>
public async Task<byte[]> GetThumb(string id, int width=300) {
var photo = await _photoRepo.Where(a => a.Id == id).FirstAsync();
using (var image = new MagickImage(GetPhotoFilePath(photo))) {
image.Format = MagickFormat.Pjpeg;
image.Resize(width,0);
return image.ToByteArray();
}
}
Then write an API
Edit StarBlog.Web/Apis/PhotoController.cs
[HttpGet("{id}/Thumb")]
public async Task<IActionResult> GetThumb(string id, [FromQuery] int width = 300) {
var data = await _photoService.GetThumb(id, width);
return new FileContentResult(data, "image/jpeg");
}
Done.
Now you just need to visit /Api/Photo/{图片ID}/Thumb?width=300 to generate a progressive JPEG thumbnail~
Finally, update the StarBlog.Web/Views/Shared/Widgets/PhotoCard.cshtml component
<img class="bd-placeholder-img card-img-top" alt=""
src="@Url.Action("GetThumb", "Photo", new {id = Model.Id, width = 300})">
okk~
Summary
Compared to the ImageSharp.Web middleware's built-in caching, this thumbnail method I wrote myself is rather crude, and it's generated dynamically on every request, which puts some pressure on the server.
But it doesn't have Progressive JPEG, and this missing feature is really frustrating; I can only hope they implement it soon~
Otherwise I'll have to implement caching myself to improve performance~
Series Articles
- Developing a Blog Project with .NetCore - StarBlog - (1) Why Do You Need to Write Your Own Blog?
- Developing a Blog Project with .NetCore - StarBlog - (2) Environment Setup and Creating the Project
- Developing a Blog Project with .NetCore - StarBlog - (3) Model Design
- Developing a Blog Project with .NetCore - StarBlog - (4) Batch Import of Markdown Blog Posts
- Developing a Blog Project with .NetCore - StarBlog - (5) Starting to Build the Web Project
- Developing a Blog Project with .NetCore - StarBlog - (6) Page Development: Blog Post List
- Developing a Blog Project with .NetCore - StarBlog - (7) Page Development: Post Detail Page
- Developing a Blog Project with .NetCore - StarBlog - (8) Displaying Category Hierarchy
- Developing a Blog Project with .NetCore - StarBlog - (9) Batch Import of Images
- Developing a Blog Project with .NetCore - StarBlog - (10) Image Waterfall Layout
- Developing a Blog Project with .NetCore - StarBlog - (11) Implementing Visit Statistics
- Developing a Blog Project with .NetCore - StarBlog - (12) Dynamic Compilation of Razor Pages
- Developing a Blog Project with .NetCore - StarBlog - (13) Adding a Friend Links Feature
- Developing a Blog Project with .NetCore - StarBlog - (14) Implementing Theme Switching
- Developing a Blog Project with .NetCore - StarBlog - (15) Generating Random-Size Images
- Developing a Blog Project with .NetCore - StarBlog - (16) Some New Features (Monitoring/Statistics/Configuration/Initialization)
- Developing a Blog Project with .NetCore - StarBlog - (17) Automatically Downloading External Images in Posts
- Developing a Blog Project with .NetCore - StarBlog - (18) Packaging and Uploading Local Typora Posts
- Developing a Blog Project with .NetCore - StarBlog - (19) Exploring Markdown Rendering Solutions
- Developing a Blog Project with .NetCore - StarBlog - (20) Image Display Optimization
References
_EOF_
StarBlog Development Notes (20): Image Display Optimization
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
StarBlog博客开发笔记(16):一些新功能
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetCore开发博客项目 StarBlog - (3) 模型设计 基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入 基于.NetCore开发博客项目 StarBl
2022-08-13 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(27):使用JWT保护接口
前言 这是StarBlog系列在2023年的第二篇更新😂 这几个月都在忙,更新变得很不勤快,但是拖着不更新我的心里更慌,很久没写,要开头就变得很难😑 说回正题,之前的文章里,我们已经把博客关键的接口都开发完成了,但还少了一个最关键的「认证授权」,少了这东西,网站就跟筛子一样,谁都可以来添加和删除数据,乱套了~ 关于「认证授权」的知识,会比较复杂,要学习这块的话,建议分几步: 基础概念 AspN
2023-05-03 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(19):Markdown渲染方案探索
前言 笔者认为,一个博客网站,最核心的是阅读体验。 在开发StarBlog的过程中,最耗时的恰恰也是文章的展示部分功能。 最开始还没研究出来如何很好的使用后端渲染,所以只能先用Editor.md组件做前端渲染,过渡一下。前端渲染我是不满意的,因为性能较差,页面加载出来还会闪一下,有割裂感,影响体验。 现在我已经做出了比较完善的后端渲染方案,前端渲染就可以直接退休了。本文介绍StarBlog博客开发
2022-10-28 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(22):开发博客文章相关接口
前言 本文介绍博客文章相关接口的开发,作为接口开发介绍的第一篇,会写得比较详细,以抛砖引玉,后面的其他接口就粗略带过了,着重于WebApi开发的周边设施。 涉及到的接口:文章CRUD、置顶文章、推荐文章等。 开始前先介绍下AspNetCore框架的基础概念,MVC模式(前后端不分离)、WebApi模式(前后端分离),都是有Controller的。 区别在前者的Controller集成自 Contr
2023-01-02 · StarBlog博客开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…