0%

Golang 在 Windows 上同时编译64位和32位可执行程序

需要在windows上同时编译需要64位的系统,32位只能编译32位的可执行程序。

  • 编译32位

    1
    GOOS=windows GOARCH=386 go build main.go
  • 编译64位

    1
    GOOS=windows GOARCH=amd64 go build main.go

    遇到的问题

    由于在项目中用到了sqlite所以在编译的时候报 exec: “gcc”: executable file not found in %PATH%,这是因为在windows上缺少 gcc,所以在windows 上需要安装 _MinGW_。

  • 下载 MinGW
    进入下载页面

  • 解压 MinGW32和MinGW64
    注意需要解压到不同地方,可以平级但是千万不要在同一个目录

  • 编译
    在编译之前先设置 MinGW 的环境变量,因为是一次性的所以直接用命令行来设置

    • 64位编译
      请注意C:MinGW64bin 应该改为你的64位MinGW路径
      1
      2
      set PATH=C:\MinGW64\bin;%PATH%
      GOOS=windows GOARCH=386 go build main.go
      • 32位编译
        请注意C:MinGW32bin 应该改为你的32位MinGW路径
        1
        2
        set PATH=C:\MinGW32\bin;%PATH%
        GOOS=windows GOARCH=amd64 go build main.go

大公告成

在 Spring MVC 中上传文件后把文件保存在本地,但是数据库保存了一份路径,所有需要实现下载文件,网上的教程一般都是自己写一个Controller 去下载,但是我这里直接使用Spring MVC的 ResourceHttpRequestHandler 实现动态下载静态资源,只需一点点代码然后加一段配置就可以实现下载附件功能。

第一步

自定义一个Handler,并重写 ResourceHttpRequestHandlerprocessPath 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Slf4j
public class CustomResourceHttpRequestHandler extends ResourceHttpRequestHandler {
private final UploadRepository uploadRepository;

public CustomResourceHttpRequestHandler(UploadRepository uploadRepository) {
this.uploadRepository = uploadRepository;
}
// 重写该方法实现动态获取文件路径,而不是直接获取本地文件的路径
@Override
protected String processPath(String path) {
Optional<Upload> uploadOption = uploadRepository.findById(path);
return uploadOption.map(Upload::getPath)
.orElseGet(() -> {
log.warn("文件获取path不存在。path={}", path);
return null;
});
}
}

第二部配置

在 Spring Boot 中添加一下配置,注意LocationValues 的路径,必须以 / 结尾,访问本地文件必须有 file: ,不然会访问不到,报404

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Bean
public ResourceHttpRequestHandler customResourceHttpRequestHandler(UploadRepository uploadRepository) {
ResourceHttpRequestHandler requestHandler = new
CustomResourceHttpRequestHandler(uploadRepository);
requestHandler.setLocationValues(Collections
.singletonList("file:/D:/upload/"));
// 资源缓存配置,缓存90天
requestHandler.setCacheControl(CacheControl.maxAge(90, TimeUnit.DAYS));
return requestHandler;
}

@Bean
public SimpleUrlHandlerMapping sampleServletMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MAX_VALUE - 2);
Properties urlProperties = new Properties();
urlProperties.put("/files/*", "customResourceHttpRequestHandler");
mapping.setMappings(urlProperties);
return mapping;
}

测试

在地址栏输入地址,最后一位是数据库中 附件的id

http://localhost:8080/files/{id}

第一种方式,使用脚本(不推荐)

使用命令行开启和关闭,直接 kill 掉Spring boot 进程,这样会导致Spring Cloud 项目不会去 注册中心把自己销毁掉,明明项目已经关闭,但是注册中心还是显示在线,而且这样还会有一个弊端,如果需
要监听Spring Boot 项目的生命周期钩子,比如项目启动和关闭,也会监听不到。
/home/dev/app.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/bin/bash
APP_NAME=$2
#使用说明,用来提示输入参数
usage() {
echo "Usage: sh 执行脚本.sh [start|stop|restart|status]"
exit 1
}

#检查程序是否在运行
is_exist(){
count=`ps -ef |grep java|grep $APP_NAME|grep -v grep|wc -l`
#如果不存在返回1,存在返回0
if [ $count == 0 ]; then
return 1
else
return 0
fi
}

#启动方法
start(){
is_exist
if [ $? -eq "0" ]; then
echo "${APP_NAME} is already running. pid=${pid} ."
else
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
JAR_PATH="$SHELL_FOLDER/../applications/$APP_NAME.jar"
CONF_PATH="$SHELL_FOLDER/../conf/logback-spring.xml"
nohup java -jar $JAR_PATH --logging.config=$CONF_PATH --spring.profiles.active=test > /dev/null 2>&1 &
# java -jar $JAR_PATH --logging.config=$CONF_PATH
fi
}

#停止方法
stop(){
is_exist
if [ $? -eq "0" ]; then
pid=`ps -ef |grep java|grep $APP_NAME|grep -v grep|awk '{print $2}'`
echo "Stop $APP_NAME, pid $pid"
kill -9 $pid
else
echo "${APP_NAME} is not running"
fi
}

#输出运行状态
status(){
is_exist
if [ $? -eq "0" ]; then
echo "${APP_NAME} is running. Pid is ${pid}"
else
echo "${APP_NAME} is NOT running."
fi
}

#重启
restart(){
stop
start
}

#根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
*)
usage
;;
esac

使用:

1
2
3
4
5
6
开启
./app start myapp.jar
关闭
./app stop myapp.jar
# 重启
./app.sh restart myapp.jar

第二种方式,打包可执行jar(推荐)

打包可执行的jar可以支持 systemctl 启动,也可以支持脚本启动,并且执行关闭后会冲注册中心销毁掉服务,并且会在启动和关闭会调用Spring Boot 的生命周期事件。

  1. 编译工具配置

    • Maven
      1
      2
      3
      4
      5
      6
      7
      <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
      <executable>true</executable>
      </configuration>
      </plugin>
    • Gradle
      1
      2
      3
      bootJar {
      launchScript()
      }
      修改之后然后重新打包
  2. systemd 配置

    • 添加 systemd 的配置文件
      /etc/systemd/system/my-app.service
      注意:多个服务请创建多个文件
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      [Unit]
      Description=my-app.service
      After=syslog.target
      [Service]
      User=dev
      ExecStart=my-app.jar --logging.config=logback-spring.xml --spring.profiles.active=prod
      SuccessExitStatus=143
      TimeoutStopSec=10
      Restart=on-failure
      RestartSec=5

      [Install]
      WantedBy=multi-user.target
    • 启用 服务
      1
      2
      3
      4
      5
      6
      7
      8
      9
      systemctl enable my-app.service
      ## 开启服务
      systemctl start my-app
      ## 关闭服务
      systemctl stop my-app
      ## 查看服务状态
      systemctl status my-app
      ## 重启服务状态
      systemctl restart my-app
  3. 指定应用重新的内存等等
    在 my-app.jar 的同一个文件夹创建一个 my-app.conf 文件。
    注意:配置文件必须和jar包的文件名一直才生效。
    其它配置请看更多配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 指定内存为256
    JAVA_OPTS="-Xmx256m -Xms256m"
    # 指定操作模式,通常是 auto,但是我们这里使用 service
    MODE=service
    # 指定 pid文件路径
    PID_FOLDER=./pid
    # 指定日志文件夹,如果使用了logback可以指定自己的日志文件夹路径,我用了 logback,所有这个日志文件夹没是没用,但是在启动logback 之前还是会输出一点日志,所有也需要指定一下,我是放在 /tmp 目录下
    LOG_FOLDER=/tmp
    # 指定spring boot 的参数
    RUN_ARGS="--logging.config=/home/conf/logback-spring.xml --spring.profiles.active=test"
    1. 不使用 systemd 而使用脚本启动和关闭.
      该方式同样支持冲注册中心销毁并且调用生命周期事件。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      #!/bin/bash
      APP_NAME=$2
      #使用说明,用来提示输入参数
      usage() {
      echo "Usage: sh 执行脚本.sh [start|stop|restart|status]"
      exit 1
      }
      #拼接可执行 jar 的路径
      jar_path(){
      SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
      JAR_PATH="$SHELL_FOLDER/../applications/$APP_NAME.jar"
      return $JAR_PATH
      }

      #启动方法
      start(){
      /bin/bash `jar_path` start
      }

      #停止方法
      stop(){
      /bin/bash `jar_path` stop
      }

      #输出运行状态
      status(){
      /bin/bash `jar_path` status
      }

      #重启
      restart(){
      /bin/bash `jar_path` restart
      }

      #根据输入参数,选择执行对应方法,不输入则执行使用说明
      case "$1" in
      "start")
      start
      ;;
      "stop")
      stop
      ;;
      "status")
      status
      ;;
      "restart")
      restart
      ;;
      *)
      usage
      ;;
      esac