简体   繁体   中英

Images GETs throw 404 error in production using Webpack and AngularJS

I have this conundrum I can't solve after many tryouts:

I'm unable to load static images in production;

everything is fine while developing ( npm run serve ), even when serving files from dist ( npm run serve:dist )

Versions

Webpack: 3.12.0

file-loader: 1.1.11

url-loader: 1.1.2

AngularJS: 1.6.9

While developing:

My index.html contains this line and webpack won't load without

<base href="/"></base>

while my AngularJS config block contains

$locationProvider.hashPrefix('');

I use an URL like http://localhost:3000/#/appName/listingComponent/users

and I'm able to see every image using the ng-src (sometimes it's dynamically binded) like this

<img ng-src="app/images/image1.png">

the folder structure is like this

ui/
├── conf/
│   ├── browsersync-dist.conf.js
│   ├── browsersync.conf.js
│   ├── gulp.conf.js
│   ├── webpack-dist.conf.js
│   └── webpack.conf.js
├── gulp_tasks/
│   ├── browsersync.js
│   ├── misc.js
│   └── webpack.js
├── node_modules/
├── src/
│   ├── app/
│   │   ├── config.js  //global variables
│   │   ├── favicon.ico
│   │   ├── index.html
│   │   ├── index.js
│   │   ├── index.less
│   │   ├── images/ <--
│   │   ├── actual_app_folders/
│   │   └── ...
├── gulpfile.js
└── package.json

In my webpack-dist.js I have the following

module.exports = {
  module: {
    loaders: [
      {
        test: /\.json$/,
        loaders: [
          'json-loader'
        ]
      },
      {
        test: /\.js$/,
        exclude: [
          /node_modules/
        ],
        loader: 'babel-loader',
        options: {
          presets: ['es2015']
        }
      },
      {
        test: /\.js$/,
        exclude: [
          /node_modules/
        ],
        loaders: [
          'eslint-loader'
        ],
        enforce: 'pre'
      },
      {
        test: /\.(css|less)$/,
        exclude: '/node_modules/roboto-fontface/',
        loaders: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader?minimize!less-loader!postcss-loader'
        })
      },
      {
        test: /\.html$/,
        loaders: [
          'html-loader'
        ]
      },
      {
        test: /\.(jpe?g|png|gif|svg|eot|woff|ttf|svg|woff2)$/,
        loader: 'url-loader?name=[name].[ext]'
      }
    ]
  },
  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    FailPlugin,
    new HtmlWebpackPlugin({
      template: conf.path.src('/app/index.html'),
      inject: true,
      chunksSortMode: 'dependency'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendors', 'config'],
      minChunks: Infinity
    }),
    new ExtractTextPlugin('index-[contenthash].css'),
    new webpack.LoaderOptionsPlugin({
      options: {
        postcss: () => [autoprefixer]
      }
    })
  ],
  output: {
    path: conf.paths.dist,
    filename: function(output) {
      return output['chunk']['name'] === 'config' ? '[name].js' : '[name]-[hash].js';
    },
    chunkFilename: '[name]-[hash].js'
  },
  entry: {
    app: `./${conf.path.src('/app/index')}`,
    config: `./${conf.path.src('/app/config')}`,
    vendors: Object.keys(pkg.dependencies).concat(['webpack-material-design-icons'])
  }
};

IN PRODUCTION

The build process adds the /customer_company_name/our_company_name/ prefix to the path, which becomes

http://<customer_domain>:<domain_port>/customer_company_name/our_company_name/#/appName/listingComponent/users

while the folder structure is like this

our_company_name/
├── app/
│   ├── images/ <--
│   │   ├── image1.png
│   │   └── image2.png
├── app-<random_number>.js
├── config.js
├── favicon.ico
├── index.html
├── index-<random_number>.css
└── vendors.js

THE PROBLEM

Now every time there's a pic to be shown, the browser gets a 404 error like this

GET http://<customer_domain>:<domain_port>/app/images/image1.png 404 (Not Found)

while the request should be made like this

GET http://<customer_domain>:<domain_port>/customer_company_name/our_company_name/index.js

like every other file in the application does.

What I tried

So far nothing of the following helped:

  1. Change base tag upon build process in the following <base href="/customer_company_name/our_company_name/"></base> (this is currently retained)
  2. Explicitly requiring the pictures together with every other file in the index.js require('./images/ODM_trasp48px.png'); (this is currently retained)
  3. Using publicPath in webpack output.publicPath: '/customer_company_name/our_company_name/' and also output.publicPath: '/'
  4. Using file-loader instead of url-loader (this is currently retained)
  5. Using file-loader with options: { useRelativePath: true }
  6. Using file-loader with options: { publicPath: /customer_company_name/our_company_name/ }

Can you help me lads, please?

I think the problem is not with the replacement pattern since it works for other file types, but the fact that webpack does not know what to do with ng-src attribute. I think it only looks at the img's src attribute by default. You can change it to recognize ng-src like:

module.exports = {
  module: {
    loaders: [
    ...
    {
      test: /\.html$/,
      loaders: [ "html-loader?" + JSON.stringify({ attrs: ["img:src", "img:ng-src"] })]
    },
    ...
  ],
   ...

alt syntax:

{
   test: /\.html$/,
   loaders: [ "html-loader?attrs=img:src img:ng-src" ]
}

Before changing your config, you can just test whether that is the reason by checking if the replacement currently works correctly for img tags with a plain static <img src="..."/>.

Instead of letting webpack parsing your templates, you might try to explicitly import your image(s) and pass it to your template.

The imported images will be then processed by the configured image-loader or url-loader .

Static image

const myImage = require('./path/to/images/image1.png');

// In component declaration 
`<img ng-src="${myImage}">`

Dynamic image

Using webpack's require.context

const pathToImages = require.context('./path/to/images', true);
const dynamicImageName = 'foo.jpg',

// In component declaration 
`<img ng-src="${pathToImages(dynamicImageName, true)}">`

你可以尝试下面改变你的index.html

<base href="./"></base>

SOLUTION Let's start with

output.publicPath: '/customer_company_name/our_company_name/'

this will take care of rewriting ng-/src paths correctly as explained here .

We won't need the html-loader to parse ng-/src, the only thing needed is to always use ng-src, or the build won't be successful.

Eventually, there's no need of <base> tag and neither the initial slash in every path used to refer to images.

I'll update this answer as I improve the webpack-dist.conf.js /the whole project for the better.


No answer given worked per se, hence I'll try combinations of them since some suggestions look legit.

Eventually, if I won't be able to solve this, I'll try to drop the production path prefix /customer_company_name/our_company_name/ .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM