@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters)
{
converters.add(stringHttpMessageConverter());
converters.add(mappingJackson2HttpMessageConverter());
}
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer)
{
configurer.defaultContentType(MediaType.APPLICATION\_JSON);
configurer.ignoreUnknownPathExtensions(false);
configurer.favorPathExtension(true);
configurer.favorParameter(false);
final Map<String, MediaType> mediaTypes = new ConcurrentHashMap<>(3);
mediaTypes.put("atom", MediaType.APPLICATION\_ATOM\_XML);
mediaTypes.put("html", MediaType.TEXT\_HTML);
mediaTypes.put("json", MediaType.APPLICATION\_JSON);
configurer.mediaTypes(mediaTypes);
}
@Bean
StringHttpMessageConverter stringHttpMessageConverter()
{
return new StringHttpMessageConverter();
}
@Bean
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter()
{
final MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
final List<MediaType> list = new ArrayList<>();
list.add(MediaType.APPLICATION\_JSON);
list.add(MediaType.APPLICATION\_XML);
list.add(MediaType.TEXT\_PLAIN);
list.add(MediaType.TEXT\_HTML);
list.add(MediaType.TEXT\_XML);
messageConverter.setSupportedMediaTypes(list);
return messageConverter;
}
/\*\*
* 等价于mvc中<mvc:view-controller path=“/” view-name=“redirect:index” />
* 等价于mvc中<mvc:view-controller path=“/index” view-name=“index.html” />
*
* @param registry
*/
@Override
public void addViewControllers(final ViewControllerRegistry registry)
{
registry.addViewController(“/”).setViewName(“redirect:index”);
// registry.addViewController(“/index”).setViewName(“index.html”);
}
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(authInterceptor).addPathPatterns("/rest/file/\*\*", "/file/\*\*");
}
}
//goto src\main\java\com\fly\core\exception\GlobalExceptionHandler.java
package com.fly.core.exception;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.fly.web.entity.JsonResult;
import lombok.extern.slf4j.Slf4j;
/**
* 统一异常处理器
*
* @author 00fly
* @version [版本号, 2018-09-11]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler
{
@ExceptionHandler(value = Exception.class)
public JsonResult<?> handleBadRequest(Exception exception)
{
// JSR303参数校验异常
if (exception instanceof BindException)
{
BindingResult bindingResult = ((BindException)exception).getBindingResult();
if (null != bindingResult && bindingResult.hasErrors())
{
List errMsg = new ArrayList<>();
bindingResult.getFieldErrors().stream().forEach(fieldError -> {
errMsg.add(fieldError.getDefaultMessage());
});
Collections.sort(errMsg);
return JsonResult.error(StringUtils.join(errMsg, “,”));
}
}
if (exception instanceof MethodArgumentNotValidException)
{
BindingResult bindingResult = ((MethodArgumentNotValidException)exception).getBindingResult();
if (null != bindingResult && bindingResult.hasErrors())
{
List errMsg = new ArrayList<>();
bindingResult.getFieldErrors().stream().forEach(fieldError -> {
errMsg.add(fieldError.getDefaultMessage());
});
return JsonResult.error(StringUtils.join(errMsg, “,”));
}
}
// 其余情况
log.error(“Error: handleBadRequest StackTrace : {}”, exception);
return JsonResult.error(StringUtils.defaultString(exception.getMessage(), “系统异常,请联系管理员”));
}
}
//goto src\main\java\com\fly\core\exception\ValidateException.java
package com.fly.core.exception;
public class ValidateException extends RuntimeException
{
private static final long serialVersionUID = -939208231165751812L;
public ValidateException()
{
super();
}
public ValidateException(String message)
{
super(message);
}
}
//goto src\main\java\com\fly\core\qr\BufferedImageLuminanceSource.java
package com.fly.core.qr;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import com.google.zxing.LuminanceSource;
public class BufferedImageLuminanceSource extends LuminanceSource
{
private BufferedImage image;
private int left;
private int top;
public BufferedImageLuminanceSource(BufferedImage image)
{
this(image, 0, 0, image.getWidth(), image.getHeight());
}
public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height)
{
super(width, height);
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
if (left + width > sourceWidth || top + height > sourceHeight)
{
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
}
for (int y = top; y < top + height; y++)
{
for (int x = left; x < left + width; x++)
{
if ((image.getRGB(x, y) & 0xFF000000) == 0)
{
image.setRGB(x, y, 0xFFFFFFFF);
}
}
}
this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE\_BYTE\_GRAY);
this.image.getGraphics().drawImage(image, 0, 0, null);
this.left = left;
this.top = top;
}
@Override
public byte[] getRow(int y, byte[] row)
{
if (y < 0 || y >= getHeight())
{
throw new IllegalArgumentException("Requested row is outside the image: " + y);
}
int width = getWidth();
if (row == null || row.length < width)
{
row = new byte[width];
}
image.getRaster().getDataElements(left, top + y, width, 1, row);
return row;
}
@Override
public byte[] getMatrix()
{
int width = getWidth();
int height = getHeight();
int area = width \* height;
byte[] matrix = new byte[area];
image.getRaster().getDataElements(left, top, width, height, matrix);
return matrix;
}
@Override
public boolean isCropSupported()
{
return true;
}
@Override
public LuminanceSource crop(int left, int top, int width, int height)
{
return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);
}
@Override
public boolean isRotateSupported()
{
return true;
}
@Override
public LuminanceSource rotateCounterClockwise()
{
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);
BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE\_BYTE\_GRAY);
Graphics2D g = rotatedImage.createGraphics();
g.drawImage(image, transform, null);
g.dispose();
int width = getWidth();
return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);
}
}
//goto src\main\java\com\fly\core\qr\QRCodeUtil.java
package com.fly.core.qr;
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
public class QRCodeUtil
{
/**
* 二维码尺寸
*/
private static final int QRCODE_SIZE = 300;
/\*\*
* LOGO宽度
*/
private static final int WIDTH = 60;
/\*\*
* LOGO高度
*/
private static final int HEIGHT = 60;
/\*\*
* 给定内容、图标生成二维码图片
*
* @param content 內容
* @param imgURL 图标
* @param needCompress 是否压缩尺寸
* @return
* @throws Exception
* @see [类、类#方法、类#成员]
*/
public static BufferedImage createImage(String content, URL imgURL, boolean needCompress)
throws Exception
{
Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
if (imgURL == null)
{
return image;
}
// 插入图片
insertImage(image, imgURL, needCompress);
return image;
}
private static void insertImage(BufferedImage source, URL imgURL, boolean needCompress)
throws Exception
{
if (imgURL == null)
{
System.err.println("文件不存在!");
return;
}
Image src = ImageIO.read(imgURL);
int width = src.getWidth(null);
int height = src.getHeight(null);
if (needCompress)
{
// 压缩LOGO
width = Math.min(width, WIDTH);
height = Math.min(height, HEIGHT);
Image image = src.getScaledInstance(width, height, Image.SCALE\_SMOOTH);
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE\_INT\_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
src = image;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QRCODE\_SIZE - width) / 2;
int y = (QRCODE\_SIZE - height) / 2;
graph.drawImage(src, x, y, width, height, null);
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
/\*\*
* 解析二维码图
*
* @param file
* @return
* @throws Exception
* @see [类、类#方法、类#成员]
*/
public static String decode(File file)
throws Exception
{
BufferedImage image = ImageIO.read(file);
if (image == null)
{
return null;
}
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Hashtable<DecodeHintType, String> hints = new Hashtable<>();
hints.put(DecodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
Result result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
/\*\*
* 解析二维码图
*
* @param path
* @return
* @throws Exception
* @see [类、类#方法、类#成员]
*/
public static String decode(String path)
throws Exception
{
return decode(new File(path));
}
}
//goto src\main\java\com\fly\core\utils\TokenUtils.java
package com.fly.core.utils;
import java.util.Date;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TokenUtils
{
private static String sysToken;
/\*\*
* sysToken有效时间(2小时)
*/
private static Date sysTokenTime;
/\*\*
* 验证token是否合法
*
* @param token
* @return
* @return
*/
public static boolean valide(String token)
{
boolean success = StringUtils.equals(token, getToken());
if (!success)
{
log.info(“------ now valid sysToken is: {}”, sysToken);
}
return success;
}
/\*\*
* 获取sysToken有效时间
*
* @return
*/
public static String getTokenTime()
{
if (sysTokenTime != null)
{
return DateFormatUtils.format(sysTokenTime, “yyyy-MM-dd HH:mm:ss”);
}
return null;
}
/\*\*
* 获取sysToken
*
* @return
* @see [类、类#方法、类#成员]
*/
private static String getToken()
{
Date now = new Date();
if (sysTokenTime == null || now.after(sysTokenTime))
{
sysTokenTime = DateUtils.addHours(now, 2);
sysToken = UUID.randomUUID().toString().replace(“-”, “”);
log.info(“------ now valid sysToken is: {}”, sysToken);
}
return sysToken;
}
}
//goto src\main\java\com\fly\FilesSendBootApplication.java
package com.fly;
import java.net.InetAddress;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class FilesSendBootApplication
{
@Value(“${server.port}”)
String port;
public static void main(String[] args)
{
SpringApplication.run(FilesSendBootApplication.class, args);
}
@Bean
@ConditionalOnWebApplication
CommandLineRunner init()
{
return args -> {
if (SystemUtils.IS\_OS\_WINDOWS)// 防止非windows系统报错,启动失败
{
String ip = InetAddress.getLocalHost().getHostAddress();
String url = "http://" + ip + ":" + port;
Runtime.getRuntime().exec("cmd /c start " + url);
}
};
}
}
//goto src\main\java\com\fly\web\controller\ApiController.java
package com.fly.web.controller;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fly.core.qr.QRCodeUtil;
import com.fly.core.utils.TokenUtils;
import com.fly.web.entity.JsonResult;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
@Api(tags = “系统接口”)
@RestController
@RequestMapping(“/api”)
public class ApiController
{
@Autowired
HttpSession httpSession;
@ApiOperationSupport(order = 10)
@PostMapping("/login")
@ApiOperation("登录系统")
public JsonResult<?> login(String token)
{
if (!TokenUtils.valide(token))
{
return JsonResult.error("token empty or valide failed!");
}
httpSession.setAttribute("token", token);
String date = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss");
return JsonResult.success(date + " login success!");
}
@ApiOperationSupport(order = 20)
@PostMapping("/logout")
@ApiOperation("退出系统")
public JsonResult<?> logout()
{
httpSession.invalidate();
String date = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss");
return JsonResult.success(date + " logout success!");
}
@ApiOperation("生成二维码")
@ApiImplicitParam(name = "content", value = "二维码文本", required = true, example = "乡愁是一棵没有年轮的树,永不老去")
@PostMapping(value = "/qr/create", produces = MediaType.IMAGE\_JPEG\_VALUE)
public void index(String content, HttpServletResponse response)
throws Exception
{
if (StringUtils.isNotBlank(content))
{
Resource resource = new ClassPathResource("img/dog.jpg");
URL imgURL = resource.getURL();
BufferedImage image = QRCodeUtil.createImage(content, imgURL, true);
// 输出图象到页面
ImageIO.write(image, "JPEG", response.getOutputStream());
}
}
}
//goto src\main\java\com\fly\web\controller\file\FileController.java
package com.fly.web.controller.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import com.fly.core.exception.ValidateException;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
@RequestMapping(“/file”)
public class FileController
{
@PostMapping(“/upload”)
public String upload(@RequestParam MultipartFile[] files)
throws IOException
{
if (files == null || files.length == 0)
{
throw new ValidateException(“files is null”);
}
String date = DateFormatUtils.format(System.currentTimeMillis(), “yyyyMMdd”);
String dir = new File(“upload”).getCanonicalPath() + File.separator + date + File.separator;
new File(dir).mkdirs();
// 保存文件
for (MultipartFile file : files)
{
if (StringUtils.isNotBlank(file.getOriginalFilename()))
{
File newFile = new File(dir + file.getOriginalFilename());
FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(newFile));
log.info("###### file upload to: {}", dir);
}
}
return "redirect:/index";
}
@GetMapping(value = "/down/{index}", produces = MediaType.APPLICATION\_OCTET\_STREAM\_VALUE)
public void down(@PathVariable int index, HttpServletResponse response)
throws IOException
{
File dir = new File("upload");
List<File> files = FileUtils.listFiles(dir, null, true).stream().filter(f -> f.isFile()).sorted(Comparator.comparing(File::getAbsolutePath)).collect(Collectors.toList());
if (index >= 0 && index < files.size())
{
File file = files.get(index);
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), StandardCharsets.UTF\_8.name()));
response.setHeader("Cache-Control", "no-store, no-cache");
FileCopyUtils.copy(new FileInputStream(file), response.getOutputStream());
}
}
@GetMapping(value = "/clear")
public String clear()
throws IOException
{
File dir = new File("upload");
FileUtils.cleanDirectory(dir);
return "redirect:/index";
}
@GetMapping(value = "/delete/{index}")
public String delete(@PathVariable int index)
{
File dir = new File("upload");
List<File> files = FileUtils.listFiles(dir, null, true).stream().filter(f -> f.isFile()).sorted(Comparator.comparing(File::getAbsolutePath)).collect(Collectors.toList());
if (index >= 0 && index < files.size())
{
files.get(index).delete();
}
return "redirect:/index";
}
}
//goto src\main\java\com\fly\web\controller\file\RestFileController.java
package com.fly.web.controller.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.fly.core.exception.ValidateException;
import com.fly.web.entity.JsonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Api(tags = “文件上传、下载接口”)
@RestController
@RequestMapping(“/rest/file”)
public class RestFileController
{
@ApiOperation(“文件下载, index取值 [0, files.length)”)
@ApiImplicitParam(name = “index”, value = “文件索引,起始值0”, required = true, allowableValues = “0,1,2,3,4,5,6,7,8,9,10”)
@GetMapping(value = “/down/{index}”, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void down(@PathVariable int index, HttpServletResponse response)
throws IOException
{
File dir = new File(“upload”);
List files = FileUtils.listFiles(dir, null, true).stream().filter(f -> f.isFile()).sorted(Comparator.comparing(File::getAbsolutePath)).collect(Collectors.toList());
if (index >= 0 && index < files.size())
{
File file = files.get(index);
response.setHeader(“Content-Disposition”, “attachment;filename=” + URLEncoder.encode(file.getName(), StandardCharsets.UTF_8.name()));
response.setHeader(“Cache-Control”, “no-store, no-cache”);
FileCopyUtils.copy(new FileInputStream(file), response.getOutputStream());
}
}
@ApiOperation("文件搜索")
@PostMapping("/list")
public JsonResult<?> list()
{
File dir = new File("upload");
if (!dir.exists() || !dir.isDirectory())
{
return JsonResult.error("文件目录不存在");
}
// 检索文件路径排序
List<String> paths = FileUtils.listFiles(dir, null, true).stream().filter(f -> f.isFile()).map(f -> f.getPath()).sorted().collect(Collectors.toList());
return JsonResult.success(paths);
}
@ApiOperation("文件批量上传处理")
@PostMapping("/upload")
public JsonResult<?> upload(MultipartFile[] files)
throws IOException
{
if (files == null || files.length == 0)
{
throw new ValidateException("文件不能为空");
}
String date = DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd");
String dir = new File("upload").getCanonicalPath() + File.separator + date + File.separator;
new File(dir).mkdirs();
// 保存文件
for (MultipartFile file : files)
{
File newFile = new File(dir + file.getOriginalFilename());
FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(newFile));
}
if (SystemUtils.IS\_OS\_WINDOWS)
{
Runtime.getRuntime().exec("cmd /c start " + dir);
}
else
{
log.info("###### file upload to: {}", dir);
}
return JsonResult.success("文件上传成功,保存目录:" + dir);
}
}
//goto src\main\java\com\fly\web\controller\IndexController.java
package com.fly.web.controller;
import java.io.File;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpSession;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.fly.core.utils.TokenUtils;
@Controller
public class IndexController
{
@Autowired
HttpSession httpSession;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Linux运维工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Linux运维知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip1024b (备注Linux运维获取)
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-pClkGKwb-1712347665420)]
[外链图片转存中…(img-GuoqspRo-1712347665421)]
[外链图片转存中…(img-oEN9L3fu-1712347665421)]
[外链图片转存中…(img-Ky72fKHK-1712347665422)]
[外链图片转存中…(img-ZjyrMoRB-1712347665422)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Linux运维知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip1024b (备注Linux运维获取)
[外链图片转存中…(img-PZd3I9Nl-1712347665422)]
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算